diff --git a/.github/workflows/riscv-bench.yml b/.github/workflows/riscv-bench.yml new file mode 100644 index 000000000..59a0ea13d --- /dev/null +++ b/.github/workflows/riscv-bench.yml @@ -0,0 +1,124 @@ +name: RISC-V bench + +# zkVM execute is ~10+ min (toolchain installs + host builds + emulation), so it +# is kept off the per-PR path: this workflow runs only on pushes to main (and on +# manual dispatch). It compiles the `minimal.ixe` fixture, then executes the +# kernel typecheck of one constant in the SP1 and Zisk VMs — in parallel jobs. +on: + push: + branches: main + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + # Compile a tiny env once (ix is already built here) and hand it to the zkVM + # execute jobs via artifact, so those jobs stay Lean-free. + compile-fixture: + name: Compile zkVM fixture (minimal.ixe) + runs-on: warp-ubuntu-latest-x64-16x + steps: + - uses: actions/checkout@v6 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + - uses: leanprover/lean-action@v1 + with: + build-args: "--wfail -v" + - name: Compile zkVM test fixture (minimal.ixe) + run: lake exe ix compile Tests/MinimalDefs.lean --out minimal.ixe + - uses: actions/upload-artifact@v4 + with: + name: minimal-ixe + path: minimal.ixe + if-no-files-found: error + + # Execute the kernel typecheck of the `minimal.ixe` fixture natively (no Nix, + # no proof, no GPU). SP1 and Zisk run as independent jobs so they parallelize; + # each installs only its own toolchain via sp1up / ziskup (prebuilt binaries) + # and downloads the shared fixture. minimal.ixe carries the full Init closure, + # so we scope execution with `--constant myReflEq --skip-deps`: that + # subject-only-typechecks just the named constant, trusting its Init + # dependencies as Claim assumptions, instead of typechecking all of Init (which + # never finishes in the emulator). Each host bails non-zero on any typecheck + # failure; we also assert the `failures: 0` line. + # + # The apt list is the shared superset both backends need: the ZisK book's full + # Ubuntu list (its prebuilt cargo-zisk and proofman's C++ link OpenMPI, OpenMP, + # GMP, nlohmann-json, nasm, secp256k1, …) plus pkg-config + libssl-dev for + # SP1's host crates (openssl/bindgen). The Nix shells provided all this; a bare + # runner doesn't. Must precede the toolchain install (it runs cargo-zisk). + sp1-execute: + name: SP1 zkVM Execute + needs: compile-fixture + runs-on: warp-ubuntu-latest-x64-16x + steps: + - uses: actions/checkout@v6 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + cache-workspaces: sp1 + - name: Install system build deps + run: | + sudo apt-get update + sudo apt-get install -y \ + xz-utils jq curl build-essential qemu-system libomp-dev libgmp-dev \ + nlohmann-json3-dev protobuf-compiler uuid-dev libgrpc++-dev \ + libsecp256k1-dev libsodium-dev libpqxx-dev nasm libopenmpi-dev \ + openmpi-bin openmpi-common libclang-dev clang gcc-riscv64-unknown-elf \ + pkg-config libssl-dev + - uses: actions/download-artifact@v4 + with: + name: minimal-ixe + - name: Install SP1 toolchain (sp1up, latest) + run: | + curl -L https://sp1up.succinct.xyz | bash + ~/.sp1/bin/sp1up + echo "$HOME/.sp1/bin" >> "$GITHUB_PATH" + # The precompile-aware SP1 runner-binary is auto-built from the fork git + # dep by `sp1-core-executor-runner`'s build script — no manual override. + - name: SP1 — execute minimal.ixe (assert failures == 0) + run: | + cd sp1 + cargo run --bin sp1-host -- --execute --ixe ../minimal.ixe --constant myReflEq --skip-deps | tee only.txt + grep -qE "failures: 0\b" only.txt + + zisk-execute: + name: Zisk zkVM Execute + needs: compile-fixture + runs-on: warp-ubuntu-latest-x64-16x + steps: + - uses: actions/checkout@v6 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + cache-workspaces: zisk + - name: Install system build deps + run: | + sudo apt-get update + sudo apt-get install -y \ + xz-utils jq curl build-essential qemu-system libomp-dev libgmp-dev \ + nlohmann-json3-dev protobuf-compiler uuid-dev libgrpc++-dev \ + libsecp256k1-dev libsodium-dev libpqxx-dev nasm libopenmpi-dev \ + openmpi-bin openmpi-common libclang-dev clang gcc-riscv64-unknown-elf \ + pkg-config libssl-dev + - uses: actions/download-artifact@v4 + with: + name: minimal-ixe + - name: Install Zisk toolchain (ziskup, latest) + # `--cpu` picks the CPU build (no GPU on the runner) and `--nokey` skips + # the proving/verify keys — together they avoid ziskup's interactive + # /dev/tty prompts, and execute needs no keys. `--prefix $HOME/.zisk` + # pins the install where cargo-zisk's ZiskPaths fallback looks (the + # runner sets XDG_CONFIG_HOME, which would otherwise relocate it). + run: | + curl -L https://raw.githubusercontent.com/0xPolygonHermez/zisk/main/ziskup/install.sh \ + | bash -s -- --cpu --nokey -y --prefix "$HOME/.zisk" + echo "$HOME/.zisk/bin" >> "$GITHUB_PATH" + - name: Zisk — execute minimal.ixe (assert failures == 0) + run: | + cd zisk + ulimit -l unlimited 2>/dev/null || true + cargo run --bin zisk-host -- --execute --ixe ../minimal.ixe --constant myReflEq --skip-deps | tee only.txt + grep -qE "failures: 0\b" only.txt diff --git a/.gitignore b/.gitignore index 79c705126..563e7a4e8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ **/.lake # Rust -/target +**/target # Nix result* diff --git a/Benchmarks/Compile/CompileInit.lean b/Benchmarks/Compile/CompileInit.lean new file mode 100644 index 000000000..e1f2b647a --- /dev/null +++ b/Benchmarks/Compile/CompileInit.lean @@ -0,0 +1 @@ +import Init diff --git a/Benchmarks/Compile/README.md b/Benchmarks/Compile/README.md index 779ad7921..28278aace 100644 --- a/Benchmarks/Compile/README.md +++ b/Benchmarks/Compile/README.md @@ -10,7 +10,7 @@ Test libraries for the Ix compiler First ensure the Lean version used to build Ix matches the `Benchmarks/Compile/lean-toolchain` version (check against `ix --version`). Then run -`ix compile --path /path/to/Compile.lean` # replace `` with `InitStd`, `Lean`, `Mathlib`, or `FLT` +`ix compile /path/to/Compile.lean` # replace `` with `Init`, `InitStd`, `Lean`, `Mathlib`, or `FLT` > [!NOTE] > Compiling Mathlib and FLT currently requires a multi-core CPU and >64 GB RAM. diff --git a/Benchmarks/Compile/lakefile.toml b/Benchmarks/Compile/lakefile.toml index 21330c015..db404909a 100644 --- a/Benchmarks/Compile/lakefile.toml +++ b/Benchmarks/Compile/lakefile.toml @@ -2,6 +2,9 @@ name = "Compile" version = "0.1.0" defaultTargets = ["CompileInitStd"] +[[lean_lib]] +name = "CompileInit" + [[lean_lib]] name = "CompileInitStd" diff --git a/Benchmarks/CompileFC/README.md b/Benchmarks/CompileFC/README.md index af91fc704..c10070222 100644 --- a/Benchmarks/CompileFC/README.md +++ b/Benchmarks/CompileFC/README.md @@ -16,4 +16,4 @@ This project shadows the `formal-conjectures` project's Lean version, which is n First ensure the Lean version used to build Ix matches the `Benchmarks/CompileFC/lean-toolchain` version (check against `ix --version`). Then run -`ix compile --path /path/to/CompileFC.lean` +`ix compile /path/to/CompileFC.lean` diff --git a/Cargo.lock b/Cargo.lock index 93da29681..13f6bf3bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,6 +46,20 @@ dependencies = [ "memchr", ] +[[package]] +name = "aiur" +version = "0.1.0" +dependencies = [ + "hashbrown 0.15.5", + "indexmap", + "multi-stark", + "num-bigint", + "rayon", + "rustc-hash", + "tracing", + "tracing-texray", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -63,9 +77,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" [[package]] name = "arrayref" @@ -163,6 +177,14 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" +[[package]] +name = "bignat" +version = "0.1.0" +source = "git+https://github.com/argumentcomputer/lean-ffi.git?rev=839b405de708b80504fda39abe1402114b4135a5#839b405de708b80504fda39abe1402114b4135a5" +dependencies = [ + "num-bigint", +] + [[package]] name = "bincode" version = "2.0.1" @@ -299,17 +321,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" -[[package]] -name = "chacha20" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" -dependencies = [ - "cfg-if", - "cpufeatures 0.3.0", - "rand_core 0.10.1", -] - [[package]] name = "chrono" version = "0.4.44" @@ -1721,29 +1732,61 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] -name = "ix_rs" +name = "ix-common" +version = "0.1.0" +dependencies = [ + "bignat", + "blake3", + "indexmap", + "quickcheck", + "quickcheck_macros", + "rustc-hash", +] + +[[package]] +name = "ix-compile" +version = "0.1.0" +dependencies = [ + "anyhow", + "bignat", + "blake3", + "dashmap", + "indexmap", + "itertools 0.14.0", + "ix-common", + "ix-kernel", + "ixon", + "num-bigint", + "rayon", + "rustc-hash", + "sha2 0.10.9", +] + +[[package]] +name = "ix-ffi" version = "0.1.0" dependencies = [ + "aiur", "anyhow", + "bignat", "bincode", "blake3", "bytes", "dashmap", "getrandom 0.3.4", - "hashbrown 0.15.5", "indexmap", "iroh", "iroh-base", "itertools 0.14.0", + "ix-common", + "ix-compile", + "ix-kernel", + "ixon", "lean-ffi", - "memmap2", "mimalloc", "multi-stark", "n0-error", "num-bigint", - "quickcheck", - "quickcheck_macros", - "rand 0.10.1", "rayon", "rustc-hash", "serde", @@ -1755,6 +1798,44 @@ dependencies = [ "tracing-texray", ] +[[package]] +name = "ix-kernel" +version = "0.1.0" +dependencies = [ + "bignat", + "blake3", + "dashmap", + "indexmap", + "itertools 0.14.0", + "ix-common", + "ixon", + "log", + "num-bigint", + "quickcheck", + "quickcheck_macros", + "rayon", + "rustc-hash", +] + +[[package]] +name = "ixon" +version = "0.1.0" +dependencies = [ + "bignat", + "blake3", + "dashmap", + "indexmap", + "ix-common", + "memmap2", + "num-bigint", + "quickcheck", + "quickcheck_macros", + "rayon", + "rustc-hash", + "sha2 0.10.9", + "tiny-keccak", +] + [[package]] name = "js-sys" version = "0.3.97" @@ -1776,8 +1857,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lean-ffi" version = "0.1.0" -source = "git+https://github.com/argumentcomputer/lean-ffi?rev=cc98ebf67bf453ac3827cb767f78b13ea674dd6a#cc98ebf67bf453ac3827cb767f78b13ea674dd6a" +source = "git+https://github.com/argumentcomputer/lean-ffi.git?rev=839b405de708b80504fda39abe1402114b4135a5#839b405de708b80504fda39abe1402114b4135a5" dependencies = [ + "bignat", "bindgen", "cc", "num-bigint", @@ -2708,19 +2790,17 @@ checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pkarr" -version = "5.0.5" +version = "5.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0db5bc018bd8e26cb7e7913623292e5eddd71caf29801ea2b2bd627167044e05" +checksum = "2f950360d31be432c0c9467fba5024a94f55128e7f32bc9d32db140369f24c77" dependencies = [ "base32", "bytes", "cfg_aliases", "document-features", - "ed25519", "ed25519-dalek", "getrandom 0.4.2", "ntimestamp", - "pkcs8", "self_cell", "serde", "simple-dns", @@ -3001,7 +3081,6 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ - "chacha20", "getrandom 0.4.2", "rand_core 0.10.1", ] @@ -3392,9 +3471,9 @@ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" [[package]] name = "simple-dns" -version = "0.11.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df350943049174c4ae8ced56c604e28270258faec12a6a48637a7655287c9ce0" +checksum = "dee851d0e5e7af3721faea1843e8015e820a234f81fda3dea9247e15bac9a86a" dependencies = [ "bitflags", ] diff --git a/Cargo.toml b/Cargo.toml index d1ee422ab..e3a2ae7f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,58 +1,64 @@ -[package] -name = "ix_rs" +[workspace] +members = [ + "crates/aiur", + "crates/common", + "crates/compile", + "crates/ffi", + "crates/ixon", + "crates/kernel", +] +# `zisk/` and `sp1/` are their own Cargo workspaces (guest + host) built via +# the respective zkVM toolchains; excluded so host workspace ops don't pick +# them up. +exclude = ["zisk", "sp1"] +resolver = "2" + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" + +[workspace.package] version = "0.1.0" edition = "2024" +license = "MIT OR Apache-2.0" -[lib] -crate-type = ["staticlib"] +[workspace.dependencies] +# Internal crates +aiur = { path = "crates/aiur" } +ix-common = { path = "crates/common" } +ix-compile = { path = "crates/compile" } +ixon = { path = "crates/ixon" } +ix-kernel = { path = "crates/kernel" } -[dependencies] +# lean-ffi tree (lean-ffi crate + factored-out bignat sub-crate) +bignat = { git = "https://github.com/argumentcomputer/lean-ffi.git", rev = "839b405de708b80504fda39abe1402114b4135a5" } +lean-ffi = { git = "https://github.com/argumentcomputer/lean-ffi.git", rev = "839b405de708b80504fda39abe1402114b4135a5" } + +# External shared deps anyhow = "1" blake3 = "1.8.4" -itertools = "0.14.0" -indexmap = { version = "2", features = ["rayon"] } +dashmap = "6.1.0" hashbrown = "0.15" -lean-ffi = { git = "https://github.com/argumentcomputer/lean-ffi", rev = "cc98ebf67bf453ac3827cb767f78b13ea674dd6a" } +indexmap = "2" +itertools = "0.14.0" +log = "0.4" +memmap2 = "0.9" mimalloc = { version = "0.1", default-features = false } multi-stark = { git = "https://github.com/argumentcomputer/multi-stark.git", rev = "9ecab51d553445c0cc7b571af00a76b8a83a6f8c" } num-bigint = "0.4.6" +quickcheck = "1.0.3" +quickcheck_macros = "1.0.0" rayon = "1" rustc-hash = "2" -tiny-keccak = { version = "2", features = ["keccak"] } -dashmap = { version = "6.1.0", features = ["rayon"] } -memmap2 = "0.9" sha2 = "0.10" -# Iroh dependencies -bytes = { version = "1.10.1", optional = true } -tokio = { version = "1.44.1", optional = true } -iroh = { version = "0.97", optional = true } -iroh-base = { version = "0.97", optional = true } -n0-error = { version = "0.1", optional = true } -getrandom = { version = "0.3", optional = true } +tiny-keccak = { version = "2", features = ["keccak"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } tracing-texray = { git = "https://github.com/argumentcomputer/tracing-texray", rev = "31d194dd1bc50458d26f77c89bb68f67e5d1c149" } -bincode = { version = "2.0.1", optional = true } -serde = { version = "1.0.219", features = ["derive"], optional = true } - -[dev-dependencies] -quickcheck = "1.0.3" -rand = "0.10.1" -quickcheck_macros = "1.0.0" - -[features] -default = [] -parallel = ["multi-stark/parallel"] -test-ffi = [] -net = ["bytes", "tokio", "iroh", "iroh-base", "n0-error", "getrandom", "bincode", "serde" ] - -[profile.dev] -panic = "abort" - -[profile.release] -panic = "abort" -[lints.rust] +[workspace.lints.rust] invalid_reference_casting = "warn" nonstandard_style = "warn" rust_2018_idioms = { level = "warn", priority = -1 } @@ -61,7 +67,7 @@ unreachable_pub = "warn" unused_lifetimes = "warn" unused_qualifications = "warn" -[lints.clippy] +[workspace.lints.clippy] all = { level = "warn", priority = -1 } cast_lossless = "warn" cast_possible_truncation = "warn" diff --git a/Ix/Cli/ProfileCmd.lean b/Ix/Cli/ProfileCmd.lean index 285846732..b654b79e2 100644 --- a/Ix/Cli/ProfileCmd.lean +++ b/Ix/Cli/ProfileCmd.lean @@ -1,7 +1,7 @@ /- `ix profile `: run the Ix kernel out of circuit over a serialized `.ixe` environment, recording per-block heartbeats and the delta-unfold graph - into a `.ixesp` sidecar. This is the cost model consumed by `ix shard` + into a `.ixprof` sidecar. This is the cost model consumed by `ix shard` (see `plans/sharding.md`). Recording defaults to *cache-isolated* mode: the kernel clears its @@ -29,7 +29,11 @@ def runProfileCmd (p : Cli.Parsed) : IO UInt32 := do let outPath : String := match p.flag? "out" with | some flag => flag.as! String - | none => envPath ++ ".ixesp" + -- Default sidecar mirrors the env's base name: `init.ixe` → `init.ixprof` + -- (not `init.ixe.ixprof`); a non-`.ixe` path just gets `.ixprof` appended. + | none => + let base := if envPath.endsWith ".ixe" then envPath.dropRight 4 else envPath + base ++ ".ixprof" let isolate := !(p.flag? "keep-caches" |>.isSome) let quiet := !(p.flag? "verbose" |>.isSome) @@ -52,10 +56,10 @@ end Ix.Cli.ProfileCmd open Ix.Cli.ProfileCmd in def profileCmd : Cli.Cmd := `[Cli| "profile" VIA runProfileCmd; - "Profile a `.ixe` out of circuit → `.ixesp` (sharding cost + delta graph)" + "Profile a `.ixe` out of circuit → `.ixprof` (sharding cost + delta graph)" FLAGS: - out : String; "Output .ixesp path (default: .ixesp)" + out : String; "Output .ixprof path (default: .ixprof, e.g. init.ixe → init.ixprof)" "keep-caches"; "Keep cross-constant caches: faster, lower-fidelity, may under-record" workers : Nat; "Parallel kernel workers (default: available_parallelism). Plumbs IX_KERNEL_CHECK_WORKERS." verbose; "Log every constant (default: quiet)" diff --git a/Ix/Cli/ShardCmd.lean b/Ix/Cli/ShardCmd.lean index 12ce04ae9..a3bbd1f44 100644 --- a/Ix/Cli/ShardCmd.lean +++ b/Ix/Cli/ShardCmd.lean @@ -1,11 +1,17 @@ /- - `ix shard --shards N`: partition a profiled environment into `N` - shards via recursive balanced min-cut bisection, minimizing cross-shard - delta-unfold ingress (see `plans/sharding.md`). + `ix shard `: partition a profiled environment into shards, + minimizing cross-shard delta-unfold ingress (see `plans/sharding.md`). - Reads the `.ixesp` produced by `ix profile` (pure offline graph work, so `N` - is cheap to re-tune without re-running the kernel). Writes a `.ixes` manifest - and prints a what-if report (per-shard heartbeat balance + total cross-shard + Two modes (precedence in `runShardCmd`): + - default / `--max-ram G` / `--max-cycles C`: **bin-pack to a per-shard + cycle/RAM cap** — the fewest shards that each stay under the budget, each + packed as full as the dependency structure allows (no `--max-ram` ⇒ sized to + detected system RAM). Not balanced: packing yields the minimal shard count. + - `--shards N`: force exactly `N` **balanced** min-cut shards (manual override). + + Reads the `.ixprof` produced by `ix profile` (pure offline graph work, so the + budget/`N` is cheap to re-tune without re-running the kernel). Writes a `.ixes` + manifest and prints a what-if report (per-shard cost + total cross-shard ingress). The partitioner is self-contained — no external graph-library dependency. -/ @@ -21,13 +27,9 @@ namespace Ix.Cli.ShardCmd def runShardCmd (p : Cli.Parsed) : IO UInt32 := do let some pathArg := p.positionalArg? "path" - | p.printError "error: must specify to a .ixesp file" + | p.printError "error: must specify to a .ixprof file" return 1 let espPath := pathArg.as! String - let numShards : Nat := - match p.flag? "shards" with - | some flag => flag.as! Nat - | none => 1 let balancePct : Nat := match p.flag? "balance" with | some flag => flag.as! Nat @@ -35,10 +37,35 @@ def runShardCmd (p : Cli.Parsed) : IO UInt32 := do let outPath : String := match p.flag? "out" with | some flag => flag.as! String - | none => espPath ++ ".ixes" + -- Default manifest mirrors the profile's base name: `init.ixprof` → + -- `init.ixes` (not `init.ixprof.ixes`). + | none => + let base := if espPath.endsWith ".ixprof" then espPath.dropRight 7 else espPath + base ++ ".ixes" + let shardsFlag : Option Nat := (p.flag? "shards").map (·.as! Nat) + let maxCycles : Option Nat := (p.flag? "max-cycles").map (·.as! Nat) + let maxRam : Option Nat := (p.flag? "max-ram").map (·.as! Nat) + -- Provers the prove-time estimate assumes (wall clock = max(seq/P, slowest + -- shard)). Sharded proving is sequential today, so the default is 1. + let parallelism : Nat := + match p.flag? "parallelism" with + | some flag => max 1 (flag.as! Nat) + | none => 1 - IO.println s!"Sharding {espPath} into {numShards} shards (balance ±{balancePct}%)" - rsShardEspFFI espPath (toString numShards) (toString balancePct) outPath + -- Precedence: explicit --shards (fixed count) > explicit --max-cycles/--max-ram + -- (budget) > default (size to detected system RAM). + match shardsFlag with + | some n => + IO.println s!"Sharding {espPath} into {n} shards (balance ±{balancePct}%)" + rsShardEspFFI espPath (toString n) (toString balancePct) (toString parallelism) + outPath + | none => + if maxCycles.isNone && maxRam.isNone then + IO.println s!"Sharding {espPath} to detected system RAM (balance ±{balancePct}%)" + else + IO.println s!"Sharding {espPath} to budget (max-cycles={maxCycles.getD 0}, max-ram={maxRam.getD 0} GiB, balance ±{balancePct}%)" + rsShardEspCapFFI espPath (toString (maxCycles.getD 0)) (toString (maxRam.getD 0)) + (toString balancePct) (toString parallelism) outPath if !outPath.isEmpty then IO.println s!"[shard] wrote {outPath}" return 0 @@ -48,15 +75,18 @@ end Ix.Cli.ShardCmd open Ix.Cli.ShardCmd in def shardCmd : Cli.Cmd := `[Cli| "shard" VIA runShardCmd; - "Partition a `.ixesp` into N balanced shards minimizing cross-shard delta" + "Partition a `.ixprof` into shards: pack to a RAM/cycle cap (default) or N balanced shards" FLAGS: - shards : Nat; "Number of shards N (default 1 = a single shard)" - balance : Nat; "Per-bisection balance tolerance, percent (default 5)" - out : String; "Output .ixes manifest path (default: .ixes)" + shards : Nat; "Fixed number of shards N (overrides the default budget sizing)" + "max-cycles" : Nat; "Per-shard guest-cycle budget (overrides the default RAM sizing)" + "max-ram" : Nat; "Per-shard host-RAM budget, GiB (default: detected system RAM)" + balance : Nat; "Per-bisection balance tolerance, percent (default 5)" + parallelism : Nat; "Provers assumed for the prove-time estimate (default 1 = sequential)" + out : String; "Output .ixes manifest path (default: .ixes, e.g. init.ixprof → init.ixes)" ARGS: - path : String; "Path to a .ixesp produced by `ix profile`" + path : String; "Path to a .ixprof produced by `ix profile`" ] end diff --git a/Ix/KernelCheck.lean b/Ix/KernelCheck.lean index ee0c9ca5d..f32a6fab7 100644 --- a/Ix/KernelCheck.lean +++ b/Ix/KernelCheck.lean @@ -142,7 +142,7 @@ opaque rsCheckAnonFFI : @& String → -- fail-out path ("" = none) IO (Array (String × Option CheckError)) -/-- FFI: profile a `.ixe` out of circuit, writing a `.ixesp` sidecar with +/-- FFI: profile a `.ixe` out of circuit, writing a `.ixprof` sidecar with per-block heartbeats + the delta-unfold graph (the sharding cost model, see `plans/sharding.md`). Runs the anon kernel over every checkable target. `isolate` clears the kernel's reduction-memo caches between constants for @@ -150,19 +150,34 @@ opaque rsCheckAnonFFI : @[extern "rs_kernel_profile_anon"] opaque rsProfileAnonFFI : @& String → -- .ixe path - @& String → -- .ixesp output path + @& String → -- .ixprof output path @& Bool → -- isolate caches @& Bool → -- quiet IO Unit -/-- FFI: partition a `.ixesp` into `numShards` shards, writing a `.ixes` +/-- FFI: partition a `.ixprof` into `numShards` shards, writing a `.ixes` manifest. `numShards` and `balancePct` are decimal strings (kept ABI-simple). Empty `outPath` skips the manifest. Prints a what-if report to stderr. -/ @[extern "rs_shard_esp"] opaque rsShardEspFFI : - @& String → -- .ixesp path + @& String → -- .ixprof path @& String → -- num_shards (N) @& String → -- balance percent + @& String → -- parallelism (provers for prove-time est) + @& String → -- .ixes output path ("" = skip) + IO Unit + +/-- FFI: partition a `.ixprof` to a per-shard cycle/RAM budget, writing a + `.ixes` manifest. `maxCycles` is a guest-STEP cap; if `ramGb` > 0 it is + converted via the measured prover RAM model and overrides `maxCycles`. Pass + "0" for whichever is unused. Decimal strings (ABI-simple). -/ +@[extern "rs_shard_esp_cap"] +opaque rsShardEspCapFFI : + @& String → -- .ixprof path + @& String → -- max_cycles ("0" = unset) + @& String → -- max_ram GiB ("0" = unset) + @& String → -- balance percent + @& String → -- parallelism (provers for prove-time est) @& String → -- .ixes output path ("" = skip) IO Unit diff --git a/Ix/Meta.lean b/Ix/Meta.lean index cdd62c71d..1e7e38351 100644 --- a/Ix/Meta.lean +++ b/Ix/Meta.lean @@ -96,17 +96,41 @@ def fetchMathlibCache (cwd : Option FilePath) : IO Unit := do if exitCode != 0 then throw $ IO.userError "lake exe cache get failed" +/-- Walk up from `start` looking for `lake-manifest.json`. -/ +partial def findLakeRoot (start : FilePath) : IO (Option FilePath) := do + if ← (start / "lake-manifest.json").pathExists then + return some start + match start.parent with + | none => return none + | some p => if p == start then return none else findLakeRoot p + +/-- Walk up from `cur` collecting directory names until reaching `root`, +yielding the path components between them (in top-down order). -/ +partial def collectRelParts (root cur : FilePath) (acc : List String) : Option (List String) := + if cur == root then some acc + else match cur.fileName, cur.parent with + | some name, some par => + if par == cur then none else collectRelParts root par (name :: acc) + | _, _ => none + /-- Build the Lean module at the given file path using Lake. Also fetches Mathlib cache if the project depends on it. -/ def buildFile (path : FilePath) : IO Unit := do let path ← IO.FS.realPath path - let some moduleName := path.fileStem + let some stem := path.fileStem | throw $ IO.userError s!"cannot determine module name from {path}" - fetchMathlibCache path.parent + let some parent := path.parent + | throw $ IO.userError s!"cannot determine parent of {path}" + let some root ← findLakeRoot parent + | throw $ IO.userError s!"no lake-manifest.json found at or above {parent}" + let some relParts := collectRelParts root parent [] + | throw $ IO.userError s!"{path} is not under {root}" + let moduleName := ".".intercalate (relParts ++ [stem]) + fetchMathlibCache root let child ← IO.Process.spawn { cmd := "lake" args := #["build", moduleName] - cwd := path.parent + cwd := root stdout := .inherit stderr := .inherit } diff --git a/README.md b/README.md index 0f7af7e60..d17190b0f 100644 --- a/README.md +++ b/README.md @@ -204,6 +204,404 @@ Compiler performance benchmarks are tracked at https://bencher.dev/console/proje **Rust tests:** `cargo test` or `cargo nextest run` +### Proving under SP1 + +The Ix kernel typechecker has an SP1 guest at `sp1/guest/` driven by a host at +`sp1/host/`. The workflow is two steps: first compile a Lean program to a `.ixe` +serialized `Ixon.Env`, then feed that file to the SP1 host to either execute or +prove the typecheck. + +**Nix users only:** enter the SP1 dev shell first to pick up `cargo-prove` and +the succinct Rust toolchain: + +``` +nix develop .#sp1 +``` + +Non-Nix users: install the SP1 toolchain manually per the +[SP1 docs](https://docs.succinct.xyz/docs/sp1/getting-started/install). + +1. **Compile a `.ixe` from a Lean file.** `ix compile` takes the Lean source + as a positional argument and writes the serialized `Ixon.Env`; `--out` sets + the output path (default: the lowercased input stem plus `.ixe`). For a + small demo env: + + ``` + lake exe ix compile Tests/MinimalDefs.lean --out minimal.ixe + ``` + + For a larger, realistic env compile one of the `Benchmarks/Compile` + targets, then scope proving to a single constant with `--constant` + (step 2): + + ``` + lake exe ix compile Benchmarks/Compile/CompileInit.lean --out init.ixe + ``` + +2. **Execute or prove under SP1.** From `sp1/host/`, run the host with `--ixe` + pointing at the file produced above: + + ``` + cd sp1/host + # Execute the kernel typecheck in the SP1 VM (no proof), prints failures + cycles + RUST_LOG=info cargo run --release -- --execute --ixe ../../minimal.ixe + # Generate and verify a compressed SP1 proof of the same typecheck (CPU) + WITHOUT_VK_VERIFICATION=1 RUST_LOG=info cargo run --release -- --ixe ../../minimal.ixe + # Prove a single constant out of a larger env (Anon-only): the host resolves + # the name and ships only that constant's closure sub-env. Full-closure by + # default; add --skip-deps for a subject-only check (deps trusted). + WITHOUT_VK_VERIFICATION=1 RUST_LOG=info cargo run --release -- --ixe ../../init.ixe --constant Nat.add_comm + ``` + + With no `--ixe`, the host runs against an empty `Ixon.Env`. + + **Verifying-key bypass (`WITHOUT_VK_VERIFICATION=1`).** Proving currently + requires this environment variable; `--execute` does not. The guest hashes + via the patched BLAKE3's `BLAKE3_COMPRESS` precompile (see + [`argumentcomputer/BLAKE3` branch `sp1`](https://github.com/argumentcomputer/BLAKE3/tree/sp1)), + whose recursion shapes aren't in the SP1 prover's bundled `vk_map.bin`. + Without the bypass, proving aborts with `vk not allowed` / `vk_root + mismatch`. The host is built with the SP1 fork's `experimental` feature, + which honors this variable on both the prover and the light-node verifier. + Drop it once a production `vk_map.bin` is regenerated with the new chip. + + **Kernel modes.** The default is Anon mode (metadata-erased kernel with + lazy on-demand ingress — matches Aiur's `kernel_check_test` semantics). + Pass `--meta` to run Meta mode (preserves names + dup-level-param check; + eager full-env ingress). Both prove the same structural typecheck; Meta + is strictly more constrained but slightly more expensive in cycles. + + **GPU proving (pre-production; depends on the SP1 fork).** Build with the + `cuda` feature and set `SP1_PROVER=cuda`. Beyond the stock SP1 CUDA + prerequisites (an NVIDIA GPU with CUDA 12+), the BLAKE3_COMPRESS precompile + needs two prebuilt binaries that cannot come from a crate registry and must + be produced from a local checkout of the `argumentcomputer/sp1` fork's + `blake3-precompile` branch. Set `SP1_FORK_DIR` to that checkout. Skipping + either step does not fail loudly — you get `invalid syscall number` (stale + runner) or a hang/`ConnectionRefused` (missing gpu-server). + + 1. **Runner-binary.** The host spawns execution children from + `sp1-core-executor-runner-binary`, which embeds the JIT syscall dispatch; + a stock/stale one panics `invalid syscall number` on BLAKE3_COMPRESS. + Build it from the fork and point the host at it: + + ``` + (cd "$SP1_FORK_DIR" && cargo build --release -p sp1-core-executor-runner-binary) + export SP1_CORE_RUNNER_OVERRIDE_BINARY="$SP1_FORK_DIR/target/release/sp1-core-executor-runner-binary" + ``` + + 2. **GPU server.** Install the fork's `sp1-gpu-server` once into + `~/.sp1/bin` via `$SP1_FORK_DIR/sp1-gpu/scripts/install-fork-gpu-server.sh`. + The server extracts its own embedded runner to + `/tmp/sp1-native-runner-bin-{HASH}` *only if no file is already there*, so + pre-write the fork's runner-binary to that path to win the race (the hash + is in the server binary: `strings ~/.sp1/bin/sp1-gpu-server | grep -oE + 'sp1-native-runner-bin-[0-9a-f]{64}'`). + + With both in place: + + ``` + WITHOUT_VK_VERIFICATION=1 SP1_PROVER=cuda RUST_LOG=info \ + cargo run --release --features cuda -- --ixe ../../minimal.ixe + ``` + + On an RTX PRO 6000 a `nataddcomm.ixe` compressed proof takes ~14 s on GPU vs + ~466 s on CPU. `WITHOUT_VK_VERIFICATION=1` is required as above; none of this + is production-safe until `vk_map.bin` is regenerated with the new chip. See + the [SP1 CUDA docs](https://docs.succinct.xyz/docs/sp1/generating-proofs/hardware-acceleration/cuda) + for the base CUDA prerequisites. + + **CUDA startup race (`scripts/cuda-runner.sh`).** `sp1/.cargo/config.toml` + wires every binary through `scripts/cuda-runner.sh`, which works around an + SP1 6.x CUDA-prover startup race: the SDK only retries the gpu-server socket + connect for ~1 s (CUDA init can take longer), `kill_on_drop` SIGKILLs the + server leaving an orphaned `/tmp/sp1-cuda-*.sock` → `ConnectionRefused`, and + the NVIDIA driver holds contexts for tens of seconds after a SIGKILL so a + fast retry hangs in `cuInit`. The runner cleans the stale socket and retries + with progressive backoff. It is a pass-through no-op when `SP1_PROVER` is not + `cuda`. + +### Proving under Zisk + +The Ix kernel typechecker also has a Zisk guest at `zisk/guest/` driven by a +host at `zisk/host/`. The workflow mirrors the SP1 one — first compile a Lean +program to a `.ixe`, then feed it to the Zisk host to either execute or prove +the typecheck. + +**Nix users only:** enter the Zisk dev shell first to pick up `cargo-zisk`, +`ziskemu`, and the RISC-V toolchain needed to build the guest: + +``` +nix develop .#zisk +``` + +Non-Nix users: install Zisk manually per the +[Zisk install docs](https://0xpolygonhermez.github.io/zisk/getting_started/installation.html). + +1. **Compile a `.ixe` from a Lean file.** Same as for SP1: + + ``` + lake exe ix compile Tests/MinimalDefs.lean --out minimal.ixe + ``` + +2. **Execute or prove under Zisk.** From `zisk/`, run the host with `--ixe`: + + ``` + cd zisk + # Execute the kernel typecheck in the Zisk VM (no proof), prints cycles + RUST_LOG=info cargo run --release -- --execute --ixe ../minimal.ixe + # Run the constraint checker without generating a proof + RUST_LOG=info cargo run --release -- --verify-constraints --ixe ../minimal.ixe + # Generate and verify a VadcopFinal proof of the same typecheck (CPU) + RUST_LOG=info cargo run --release -- --ixe ../minimal.ixe + # Check a single named constant out of a larger env. The host resolves the + # name and ships only its closure sub-env (lazy fault-in, no whole-env load). + # By default this is the FULL-CLOSURE typecheck — the constant and its whole + # dependency closure (matching the Aiur `bench-typecheck --constant`). + # Composes with --execute (cycles only) and plain prove. + RUST_LOG=info cargo run --release -- --execute --ixe ../init.ixe --constant Nat.add_comm + RUST_LOG=info cargo run --release -- --ixe ../init.ixe --constant Nat.add_comm + # Add --skip-deps for a subject-only check (deps trusted, not re-checked): + RUST_LOG=info cargo run --release -- --execute --ixe ../init.ixe --constant Vector.extract_append --skip-deps + ``` + + `--constant` / `--skip-deps` are the same flags the Aiur `bench-typecheck` + uses, so the two backends share one vocabulary. `--skip-deps` trusts + dependencies rather than re-checking them, so it is far cheaper than the + full-closure default — reserve it for constants too expensive to + full-closure-check that also can't be sharded (e.g. `Vector.extract_append` + over Init/Std). See [`docs/zisk-cycle-cost-model.md`](docs/zisk-cycle-cost-model.md) + for the measured cost models across constant shapes. + + With no `--ixe`, the host runs against an empty `Ixon.Env`. The host's + `build.rs` invokes `zisk_sdk::build_program("../guest")`, so `cargo run` + transparently rebuilds the guest ELF whenever its sources change. + + **Kernel mode.** The Zisk host runs Anon mode only (metadata-erased + kernel with lazy on-demand ingress); there is no `--meta` flag. + + **GPU proving.** Pass `--gpu` at runtime — Zisk's prover backend ships + with CUDA support by default (the `cpu-only` Cargo feature is opt-out + and is not set here): + + ``` + RUST_LOG=info cargo run --release -- --gpu --ixe ../minimal.ixe + ``` + + Requires a CUDA-capable GPU and the matching CUDA runtime libraries + visible to the linker (`LD_LIBRARY_PATH`). The Nix `.#zisk` shell wires + these up automatically; for non-Nix setups follow the + [Zisk install docs](https://0xpolygonhermez.github.io/zisk/getting_started/installation.html). + + **CUDA JIT cache (`CUDA_CACHE_MAXSIZE`).** The driver caches compiled + proving kernels (PTX→SASS, under `~/.nv/ComputeCache`) and reuses them + across processes — that's what keeps a second run warm. The default cap + (~1 GB) is small enough that as a run JITs more kernel variants (bigger + envs, more state-machine shapes) it forces LRU eviction and intermittent + re-JIT cold spikes mid-run. Pin it to the 4 GB hard max to remove that + failure mode (cost is only disk): + + ``` + export CUDA_CACHE_MAXSIZE=4294967296 # 4 GB + ``` + + **Warm-batch proving and cold-start.** The first GPU proof on a machine + pays a large one-time cold-start: the proving kernels are JIT-compiled + (PTX→SASS), almost entirely inside `GENERATING_INNER_PROOFS`. Measured on an + RTX PRO 6000, a `nataddcomm.ixe` proof takes **~126 s cold vs ~12 s warm** + (the inner-proof phase alone drops from ~123 s to ~9 s; EXECUTE and + `CALCULATING_CONTRIBUTIONS` are unchanged). Two things reuse that work: + `--ixe` is repeatable, so passing several inputs proves them in one process + and pays the cold-start once for the whole batch; and the JIT output is + cached on disk (`CUDA_CACHE_MAXSIZE` above), so even a *fresh* process stays + warm — the ~12 s figure above is a separate process from the cold run. So a + single small one-off proof looks slow (it eats the cold-start); amortize by + batching, or just disregard it. By default proving is stateful with no + checkpointing — if a run is killed it loses in-flight shard proofs and + restarts from the first shard; use a proof store (`--store-dir`, see + *Sharding large environments* below) to make a sharded run resumable. + + ``` + RUST_LOG=info cargo run --release -- --gpu \ + --ixe ../nataddcomm.ixe --ixe ../stringappend.ixe + ``` + + **Memlock limit (Linux).** Zisk's assembly emulator `mmap`s ROM and + trace buffers with `MAP_LOCKED`, so the per-process locked-memory + rlimit (`ulimit -l`) must be high enough to back the trace. Many + Linux distros default to a low memlock limit; the Linux kernel + itself sets non-privileged processes to `RAM / 8`, which is still + too small for larger envs. Symptom: `mmap(rom) errno=11=Resource + temporarily unavailable` followed by `Shmem creation … failed with + exit status: 255` during `STARTING_ASM_MICROSERVICES`. + + *Per-shell (does not survive new logins or reboot):* + + ``` + sudo prlimit --memlock=unlimited:unlimited --pid $$ + ulimit -l # confirm: prints 'unlimited' + ``` + + *Persistent (recommended; both edits needed because `systemd` and + PAM apply rlimits on different paths — `DefaultLimitMEMLOCK` only + reaches services systemd spawns directly, interactive shells go + through PAM):* + + ``` + # 1. systemd-spawned services + sudo sed -i 's|^#DefaultLimitMEMLOCK=.*|DefaultLimitMEMLOCK=infinity|' \ + /etc/systemd/system.conf + + # 2. PAM session limits (applied to login shells, sudo, etc.) + sudo tee /etc/security/limits.d/99-memlock.conf >/dev/null <<'EOF' + * soft memlock unlimited + * hard memlock unlimited + EOF + + # Apply: log out of every login shell and back in (no reboot needed). + # Verify in a new shell: + ulimit -l # → unlimited + cat /proc/$$/limits | grep "locked" # → unlimited / unlimited + ``` + + Matches the Zisk + [installation docs](https://0xpolygonhermez.github.io/zisk/getting_started/installation.html). + + **Heap cap.** The Zisk zkVM has a hard 512 MB RAM cap + ([`RAM_SIZE`](https://github.com/0xPolygonHermez/zisk/blob/v0.17.0/core/src/mem.rs#L111)), + of which ~510 MB is usable heap, and isn't configurable without + rebuilding the proving setup. Envs whose deserialized in-memory + representation exceeds that won't fit (full `TutorialDefs.lean` pulls in + Lean stdlib + Batteries + LSpec, around 1 GB resident). For bigger envs, + prefer the SP1 backend (default 24 GB runtime cap + [`DEFAULT_MEMORY_LIMIT`](https://github.com/succinctlabs/sp1/blob/v6.2.0/crates/core/executor/src/opts.rs#L25), + configurable via `MEMORY_LIMIT` env var up to a ~1 TB JIT ceiling + [`MAX_JIT_LOG_ADDR`](https://github.com/succinctlabs/sp1/blob/v6.2.0/crates/primitives/src/consts.rs#L11)), + or scope to a single constant with `--constant ` (all backends), + which resolves the name and ships only that constant's closure sub-env to the + guest. By default it re-checks the full dependency closure; add `--skip-deps` + to check it **subject-only** (dependencies trusted and lazily faulted in, not + re-typechecked) — so individual constants of a large env still fit the cap, + even ones whose full-closure typecheck would not. To prove a large env in + full under Zisk, shard it (see + *Sharding large environments* below): each shard ships only its own closure + sub-env, so the pieces fit the cap even when the whole env does not. + + **Host RAM cap (`--max-witness-stored`).** Distinct from the in-guest + heap cap above, the prover side (Zisk's `proofman`) holds in-flight + witness traces in host RAM during `CALCULATING_CONTRIBUTIONS`. Peak + host RAM per shard ≈ `fixed-overhead + N × avg-witness-size`, where + `N` is the `max_witness_stored` setting. With the Blake3f precompile the + Ix kernel typecheck workload measures roughly `40 GB + N × 16 GB` on + typical 200–300 kB anon-byte shards — e.g. `N = 10` peaks near 200 GB + (a `--shard-bytes 250000 --max-witness-stored 10` mergesort run completes + under a 200 GiB guard without tripping it). An earlier pre-Blake3f figure + of ~25 GB per witness is stale; the precompile shrank the witness. + + The `zisk-host` CLI defaults to `--max-witness-stored 5` (Zisk's + built-in default is 10, tuned for larger-memory boxes). Override per + machine: + + | Host RAM | `--max-witness-stored` | Notes | + | -------- | ---------------------- | ------------------------------------------------------ | + | ≤ 128 GB | `3` | Override down; consider smaller shards too | + | 256 GB | `5` (project default) | Comfortable margin on the typical setup | + | 512 GB | `10` (Zisk default) | Override up for maximum prover parallelism | + | ≥ 1 TB | `10` (Zisk default) | Override up; default is conservative for this workload | + + Lowering the cap roughly linearly bounds peak RAM but throttles + prover parallelism (~10–30 % slower in practice). Raise it if your + machine has more RAM headroom; lower it if you OOM during + `CALCULATING_CONTRIBUTIONS`. Not relevant for `--execute` or + `--verify-constraints` modes. + +3. **Sharding large environments.** A whole large env (e.g. all of + `Init`) does not fit a single Zisk proof: it overruns both the 512 MB + guest heap and, more bindingly, the prover's minimal-trace shared-memory + ceiling (64 GB per job on the assembly path). Sharding splits the env's + type-checking work into pieces that are proven independently — each + shipped with only its own dependency closure — and then folded into one + aggregate proof. This is a two-part workflow: produce a shard manifest + (a `.ixes` file), then prove it with `--shard-plan`. + + **(a) Produce a shard manifest.** A manifest partitions the env's + per-constant work, packing shards to a resource cap by real + type-checking cost and cutting where cross-shard dependencies are fewest. + + Profiling runs the kernel over the env (slow) and writes a `.ixprof` + cost map; sharding is instant graph work on that map, so it is cheap to + re-tune without re-profiling. Both steps are `ix` CLI subcommands: + + ``` + # Profile once (slow): init.ixe → init.ixprof (cost map + delta graph). + lake exe ix profile init.ixe + # Shard (instant): init.ixprof → init.ixes manifest. With no budget flags + # it bin-packs to the fewest shards that each fit this box's RAM (peak + # prover RAM ≈ `50 + 33 × cycles_billions` GB, targeting ~85 % of total). + lake exe ix shard init.ixprof + # Or pick a per-shard budget / a fixed count instead: + # --max-ram 256 --max-cycles 4500000000 --shards 16 + ``` + + `ix shard` also reports the shard count, total predicted cycles, and a + prove-time estimate from the measured leaf model (`54 s + 158 s·Bsteps` + per shard). Sharded proving is sequential today, so the estimate assumes + one prover; `--parallelism N` divides the wall-clock for a future N-prover + setup (the sequential total is always shown alongside). + + **(b) Prove the manifest.** Pass the `.ixes` to the host with + `--shard-plan` alongside the same `--ixe` it was built for. Each shard + becomes one leaf proof (built over only that shard's closure sub-env), + and the leaves fold up the manifest's aggregation tree into a single root + proof: + + ``` + cd zisk + RUST_LOG=info cargo run --release -- --gpu \ + --ixe ../init.ixe --shard-plan ../init.ixes + ``` + + **Resumable proving (`--store-dir`).** A proof store makes a sharded run + restartable and lets proofs be reused across runs and envs: + + ``` + RUST_LOG=info cargo run --release -- --gpu \ + --ixe ../init.ixe --shard-plan ../init.ixes \ + --store-dir ../init-store --require-closed + ``` + + - `--store-dir DIR`: each completed shard proof is banked to + `DIR/proofs/.{proof,cover,asm}` as it finishes. **Re-run the same + command to resume** — shards already covered by the store are skipped + (not re-proven) and folded into the aggregate instead. Reuse is + trustless: a stored proof is re-verified in-circuit at aggregation, + never trusted by the host. Proofs are content-addressed, so a constant + proven in one env is reused in any other env that contains it. + - `--no-reuse`: ignore the store for this run (the "no sharing" baseline); + the existing store is neither read nor overwritten. + - `--require-closed`: error *before* proving unless every typing + dependency of every certified constant is either proven in this run or + already in the store — axioms are the only permitted residual. Turns + "result modulo axioms" from a printed hope into an enforced + precondition. Pairs with `--plan` for a no-prove check. + + **Other shard modes and tuning flags.** + + - *Static partitioning without a manifest:* `--shard-consts N` (N work + items per shard) or `--shard-bytes B` (pack contiguous items until + serialized bytes exceed `B`). Simpler, but not balanced by real + type-checking cost — `--shard-plan` is the performant path. + - `--plan`: print the planned partition and exit, no proving. + - `--execute`: run the shards in the VM only and print per-shard cycle + counts — use it to measure a shard's true cycle cost (e.g. to pick or + recalibrate a cycle budget) without proving. + - `--only-shard K`: prove just the 1-indexed shard `K` (skips + aggregation) for smoke-testing a single shard. + - `--dump-input FILE`: write one shard's guest stdin to `FILE` for + standalone profiling with `ziskemu` or `cargo-zisk`. + - `--agg-arity A` (default 16): how many child proofs each aggregation + step folds at once. + ### Nix #### Prerequisites diff --git a/Tests/MinimalDefs.lean b/Tests/MinimalDefs.lean new file mode 100644 index 000000000..a83067cc0 --- /dev/null +++ b/Tests/MinimalDefs.lean @@ -0,0 +1,5 @@ +def myConst : Nat := 42 + +def myId (x : Nat) : Nat := x + +theorem myReflEq (n : Nat) : n = n := rfl diff --git a/crates/aiur/Cargo.toml b/crates/aiur/Cargo.toml new file mode 100644 index 000000000..3da792ae4 --- /dev/null +++ b/crates/aiur/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "aiur" +version.workspace = true +edition.workspace = true +license.workspace = true + +[dependencies] +hashbrown = { workspace = true } +indexmap = { workspace = true } +multi-stark = { workspace = true } +num-bigint = { workspace = true } +rayon = { workspace = true } +rustc-hash = { workspace = true } +tracing = { workspace = true } +tracing-texray = { workspace = true } + +[features] +default = [] +parallel = ["multi-stark/parallel"] + +[lints] +workspace = true diff --git a/src/aiur/bytecode.rs b/crates/aiur/src/bytecode.rs similarity index 85% rename from src/aiur/bytecode.rs rename to crates/aiur/src/bytecode.rs index 69b41c166..6f1b6be83 100644 --- a/src/aiur/bytecode.rs +++ b/crates/aiur/src/bytecode.rs @@ -3,23 +3,23 @@ use crate::FxIndexMap; use super::G; pub struct Toplevel { - pub(crate) functions: Vec, - pub(crate) memory_sizes: Vec, + pub functions: Vec, + pub memory_sizes: Vec, } pub struct Function { - pub(crate) body: Block, - pub(crate) layout: FunctionLayout, - pub(crate) entry: bool, - pub(crate) constrained: bool, + pub body: Block, + pub layout: FunctionLayout, + pub entry: bool, + pub constrained: bool, } #[derive(Clone, Copy)] pub struct FunctionLayout { - pub(crate) input_size: usize, - pub(crate) selectors: usize, - pub(crate) auxiliaries: usize, - pub(crate) lookups: usize, + pub input_size: usize, + pub selectors: usize, + pub auxiliaries: usize, + pub lookups: usize, } impl FunctionLayout { @@ -29,8 +29,8 @@ impl FunctionLayout { } pub struct Block { - pub(crate) ops: Vec, - pub(crate) ctrl: Ctrl, + pub ops: Vec, + pub ctrl: Ctrl, } pub enum Op { diff --git a/src/aiur/constraints.rs b/crates/aiur/src/constraints.rs similarity index 97% rename from src/aiur/constraints.rs rename to crates/aiur/src/constraints.rs index 6c687ce76..93fda0e0f 100644 --- a/src/aiur/constraints.rs +++ b/crates/aiur/src/constraints.rs @@ -7,22 +7,18 @@ use multi_stark::{ use std::{array, ops::Range, sync::LazyLock}; use crate::{ - FxIndexMap, - aiur::{ - G, - bytecode::{Block, Ctrl, Function, FunctionLayout, Op, Toplevel, ValIdx}, - function_channel, - gadgets::{ - AiurGadget, - bytes1::{Bytes1, Bytes1Op}, - bytes2::{Bytes2, Bytes2Op}, - }, - memory_channel, u8_add_channel, u8_and_channel, - u8_bit_decomposition_channel, u8_chain_rotr4_channel, - u8_chain_rotr7_channel, u8_less_than_channel, u8_mul_channel, - u8_or_channel, u8_range_check_channel, u8_shift_left_channel, - u8_shift_right_channel, u8_sub_channel, u8_xor_channel, + FxIndexMap, G, + bytecode::{Block, Ctrl, Function, FunctionLayout, Op, Toplevel, ValIdx}, + function_channel, + gadgets::{ + AiurGadget, + bytes1::{Bytes1, Bytes1Op}, + bytes2::{Bytes2, Bytes2Op}, }, + memory_channel, u8_add_channel, u8_and_channel, u8_bit_decomposition_channel, + u8_chain_rotr4_channel, u8_chain_rotr7_channel, u8_less_than_channel, + u8_mul_channel, u8_or_channel, u8_range_check_channel, u8_shift_left_channel, + u8_shift_right_channel, u8_sub_channel, u8_xor_channel, }; type Expr = SymbolicExpression; diff --git a/src/aiur/execute.rs b/crates/aiur/src/execute.rs similarity index 98% rename from src/aiur/execute.rs rename to crates/aiur/src/execute.rs index f093a3d70..11fc4df4c 100644 --- a/src/aiur/execute.rs +++ b/crates/aiur/src/execute.rs @@ -3,22 +3,19 @@ use rustc_hash::FxHashMap; use std::collections::hash_map::Entry; use crate::{ - FxIndexMap, - aiur::{ - G, - bytecode::{Block, Ctrl, FunIdx, Function, Op, Toplevel}, - gadgets::{ - AiurGadget, - bytes1::{Bytes1, Bytes1Op, Bytes1Queries}, - bytes2::{Bytes2, Bytes2Op, Bytes2Queries}, - }, - querymap::QueryMap, + FxIndexMap, G, + bytecode::{Block, Ctrl, FunIdx, Function, Op, Toplevel}, + gadgets::{ + AiurGadget, + bytes1::{Bytes1, Bytes1Op, Bytes1Queries}, + bytes2::{Bytes2, Bytes2Op, Bytes2Queries}, }, + querymap::QueryMap, }; pub struct QueryRecord { - pub(crate) function_queries: Vec, - pub(crate) memory_queries: FxIndexMap, + pub function_queries: Vec, + pub memory_queries: FxIndexMap, pub(crate) bytes1_queries: Bytes1Queries, pub(crate) bytes2_queries: Bytes2Queries, } @@ -41,17 +38,17 @@ impl QueryRecord { } } -pub(crate) struct IOKeyInfo { - pub(crate) idx: usize, - pub(crate) len: usize, +pub struct IOKeyInfo { + pub idx: usize, + pub len: usize, } pub struct IOBuffer { /// Per-channel data arenas. `idx` slots into `data[&channel]`. - pub(crate) data: FxHashMap>, + pub data: FxHashMap>, /// Channel-keyed info map; same `key` on different channels resolves /// to distinct `IOKeyInfo`. - pub(crate) map: FxHashMap<(G, Vec), IOKeyInfo>, + pub map: FxHashMap<(G, Vec), IOKeyInfo>, } impl IOBuffer { diff --git a/src/aiur/gadgets.rs b/crates/aiur/src/gadgets.rs similarity index 97% rename from src/aiur/gadgets.rs rename to crates/aiur/src/gadgets.rs index 2a3ca95f4..a71c2b9dd 100644 --- a/src/aiur/gadgets.rs +++ b/crates/aiur/src/gadgets.rs @@ -6,7 +6,7 @@ use multi_stark::{ p3_matrix::dense::RowMajorMatrix, }; -use crate::aiur::{G, execute::QueryRecord}; +use crate::{G, execute::QueryRecord}; /// A trait representing a generic Aiur gadget. /// diff --git a/src/aiur/gadgets/blake3.rs b/crates/aiur/src/gadgets/blake3.rs similarity index 99% rename from src/aiur/gadgets/blake3.rs rename to crates/aiur/src/gadgets/blake3.rs index 434f43b5a..9fd508f9e 100644 --- a/src/aiur/gadgets/blake3.rs +++ b/crates/aiur/src/gadgets/blake3.rs @@ -14,7 +14,7 @@ use multi_stark::{ p3_matrix::dense::RowMajorMatrix, }; -use crate::aiur::{ +use crate::{ G, execute::QueryRecord, gadgets::{ diff --git a/src/aiur/gadgets/bytes1.rs b/crates/aiur/src/gadgets/bytes1.rs similarity index 99% rename from src/aiur/gadgets/bytes1.rs rename to crates/aiur/src/gadgets/bytes1.rs index 1cc6525b1..cc83c5c9c 100644 --- a/src/aiur/gadgets/bytes1.rs +++ b/crates/aiur/src/gadgets/bytes1.rs @@ -6,7 +6,7 @@ use multi_stark::{ p3_matrix::dense::RowMajorMatrix, }; -use crate::aiur::{ +use crate::{ G, execute::QueryRecord, gadgets::AiurGadget, u8_bit_decomposition_channel, u8_shift_left_channel, u8_shift_right_channel, }; diff --git a/src/aiur/gadgets/bytes2.rs b/crates/aiur/src/gadgets/bytes2.rs similarity index 99% rename from src/aiur/gadgets/bytes2.rs rename to crates/aiur/src/gadgets/bytes2.rs index d8478aade..949dcff8b 100644 --- a/src/aiur/gadgets/bytes2.rs +++ b/crates/aiur/src/gadgets/bytes2.rs @@ -6,7 +6,7 @@ use multi_stark::{ p3_matrix::dense::RowMajorMatrix, }; -use crate::aiur::{ +use crate::{ G, execute::QueryRecord, gadgets::AiurGadget, u8_add_channel, u8_and_channel, u8_chain_rotr4_channel, u8_chain_rotr7_channel, u8_less_than_channel, u8_mul_channel, u8_or_channel, u8_range_check_channel, u8_sub_channel, diff --git a/src/aiur.rs b/crates/aiur/src/lib.rs similarity index 91% rename from src/aiur.rs rename to crates/aiur/src/lib.rs index 46d579ce1..605c83822 100644 --- a/src/aiur.rs +++ b/crates/aiur/src/lib.rs @@ -8,9 +8,12 @@ pub mod synthesis; pub mod trace; pub mod vk_codec; +use indexmap::IndexMap; use multi_stark::p3_field::PrimeCharacteristicRing; +use rustc_hash::FxBuildHasher; pub type G = multi_stark::p3_goldilocks::Goldilocks; +pub type FxIndexMap = IndexMap; #[inline] pub const fn function_channel() -> G { diff --git a/src/aiur/memory.rs b/crates/aiur/src/memory.rs similarity index 98% rename from src/aiur/memory.rs rename to crates/aiur/src/memory.rs index af63d4b58..738507dcb 100644 --- a/src/aiur/memory.rs +++ b/crates/aiur/src/memory.rs @@ -12,7 +12,7 @@ use rayon::{ slice::ParallelSliceMut, }; -use crate::aiur::{G, execute::QueryRecord, memory_channel}; +use crate::{G, execute::QueryRecord, memory_channel}; pub struct Memory { pub(crate) width: usize, diff --git a/src/aiur/querymap.rs b/crates/aiur/src/querymap.rs similarity index 99% rename from src/aiur/querymap.rs rename to crates/aiur/src/querymap.rs index 5bda48f71..4ae1132da 100644 --- a/src/aiur/querymap.rs +++ b/crates/aiur/src/querymap.rs @@ -1,12 +1,12 @@ use multi_stark::p3_field::PrimeField64; -use crate::aiur::G; +use crate::G; /// Immutable view of one query entry. #[derive(Clone, Copy)] pub struct QueryRef<'a> { pub(crate) output: &'a [G], - pub(crate) multiplicity: G, + pub multiplicity: G, } /// Mutable view of one query entry: the output is fixed at insertion, diff --git a/src/aiur/synthesis.rs b/crates/aiur/src/synthesis.rs similarity index 99% rename from src/aiur/synthesis.rs rename to crates/aiur/src/synthesis.rs index 9fc880713..8a8f1f1e5 100644 --- a/src/aiur/synthesis.rs +++ b/crates/aiur/src/synthesis.rs @@ -12,7 +12,7 @@ use rayon::iter::{ IntoParallelIterator, IntoParallelRefIterator, ParallelIterator, }; -use crate::aiur::{ +use crate::{ G, bytecode::{FunIdx, Toplevel}, constraints::Constraints, diff --git a/src/aiur/trace.rs b/crates/aiur/src/trace.rs similarity index 97% rename from src/aiur/trace.rs rename to crates/aiur/src/trace.rs index 9ff7857e0..e696e688b 100644 --- a/src/aiur/trace.rs +++ b/crates/aiur/src/trace.rs @@ -11,20 +11,16 @@ use rayon::{ }; use crate::{ - FxIndexMap, - aiur::{ - G, - bytecode::{Block, Ctrl, Function, Op, Toplevel}, - execute::{IOBuffer, IOKeyInfo, QueryRecord}, - function_channel, - gadgets::{bytes1::Bytes1, bytes2::Bytes2}, - memory::Memory, - u8_add_channel, u8_and_channel, u8_bit_decomposition_channel, - u8_chain_rotr4_channel, u8_chain_rotr7_channel, u8_less_than_channel, - u8_mul_channel, u8_or_channel, u8_range_check_channel, - u8_shift_left_channel, u8_shift_right_channel, u8_sub_channel, - u8_xor_channel, - }, + FxIndexMap, G, + bytecode::{Block, Ctrl, Function, Op, Toplevel}, + execute::{IOBuffer, IOKeyInfo, QueryRecord}, + function_channel, + gadgets::{bytes1::Bytes1, bytes2::Bytes2}, + memory::Memory, + u8_add_channel, u8_and_channel, u8_bit_decomposition_channel, + u8_chain_rotr4_channel, u8_chain_rotr7_channel, u8_less_than_channel, + u8_mul_channel, u8_or_channel, u8_range_check_channel, u8_shift_left_channel, + u8_shift_right_channel, u8_sub_channel, u8_xor_channel, }; struct ColumnIndex { diff --git a/src/aiur/vk_codec.rs b/crates/aiur/src/vk_codec.rs similarity index 96% rename from src/aiur/vk_codec.rs rename to crates/aiur/src/vk_codec.rs index 591263e55..f48273fbb 100644 --- a/src/aiur/vk_codec.rs +++ b/crates/aiur/src/vk_codec.rs @@ -42,7 +42,7 @@ use multi_stark::{ types::{Commitment, CommitmentParameters, Val}, }; -use crate::aiur::{ +use crate::{ constraints::Constraints, memory::Memory, synthesis::{AiurCircuit, AiurSystem}, @@ -192,9 +192,7 @@ impl W { /// Serialize the verifying key `System` (preprocessed traces are /// skipped — see the module docs). -pub(crate) fn to_bytes( - system: &System, -) -> Result, String> { +pub(crate) fn to_bytes(system: &System) -> Vec { let mut w = W { buf: Vec::new() }; w.usize(system.commitment_parameters.log_blowup); w.usize(system.commitment_parameters.cap_height); @@ -203,14 +201,12 @@ pub(crate) fn to_bytes( w.vec(&system.preprocessed_indices, |w, idx| { w.option(idx, |w, &i| w.usize(i)) }); - Ok(w.buf) + w.buf } /// Convenience: serialize the verifying key of a built [`AiurSystem`]. -pub(crate) fn aiur_system_to_bytes( - sys: &AiurSystem, -) -> Result, String> { - to_bytes(&sys.system) +pub fn aiur_system_to_bytes(sys: &AiurSystem) -> Result, String> { + Ok(to_bytes(&sys.system)) } // ════════════════════════════════════════════════════════════════════════════ @@ -381,7 +377,7 @@ pub(crate) fn from_bytes(bytes: &[u8]) -> Result, String> { #[cfg(test)] mod tests { use super::*; - use crate::aiur::gadgets::{AiurGadget, bytes1::Bytes1, bytes2::Bytes2}; + use crate::gadgets::{AiurGadget, bytes1::Bytes1, bytes2::Bytes2}; use multi_stark::{lookup::LookupAir, types::CommitmentParameters}; /// Build a small real `System` from the two byte gadgets and @@ -397,9 +393,9 @@ mod tests { LookupAir::new(AiurCircuit::Bytes2, Bytes2.lookups()), ], ); - let bytes = to_bytes(&system).expect("encode"); + let bytes = to_bytes(&system); let back = from_bytes(&bytes).expect("decode"); - let reencoded = to_bytes(&back).expect("re-encode"); + let reencoded = to_bytes(&back); assert_eq!(bytes, reencoded, "verifying-key codec round-trip mismatch"); } @@ -408,7 +404,7 @@ mod tests { let cp = CommitmentParameters { log_blowup: 1, cap_height: 0 }; let (system, _key) = System::new(cp, [LookupAir::new(AiurCircuit::Bytes1, Bytes1.lookups())]); - let mut bytes = to_bytes(&system).expect("encode"); + let mut bytes = to_bytes(&system); bytes.push(0); assert!(from_bytes(&bytes).is_err(), "should reject trailing data"); } diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml new file mode 100644 index 000000000..6ff7109c3 --- /dev/null +++ b/crates/common/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "ix-common" +version.workspace = true +edition.workspace = true +license.workspace = true + +[dependencies] +bignat = { workspace = true } +blake3 = { workspace = true } +indexmap = { workspace = true } +quickcheck = { workspace = true, optional = true } +rustc-hash = { workspace = true } + +[dev-dependencies] +quickcheck = { workspace = true } +quickcheck_macros = { workspace = true } + +[features] +quickcheck = ["dep:quickcheck"] + +[lints] +workspace = true diff --git a/src/ix/address.rs b/crates/common/src/address.rs similarity index 81% rename from src/ix/address.rs rename to crates/common/src/address.rs index df90d0614..334aafd13 100644 --- a/src/ix/address.rs +++ b/crates/common/src/address.rs @@ -36,6 +36,23 @@ impl Address { self.hash.as_bytes() } + /// Flatten a slice of addresses into a `[u8; 32 * N]` byte blob. + pub fn pack(addrs: &[Address]) -> Vec { + let mut out = Vec::with_capacity(addrs.len() * 32); + for a in addrs { + out.extend_from_slice(a.as_bytes()); + } + out + } + + /// Iterate addresses out of a flat `[u8; 32 * N]` byte blob. Trailing + /// bytes shorter than 32 are silently dropped (via `chunks_exact`). + pub fn unpack(bytes: &[u8]) -> impl Iterator + '_ { + bytes + .chunks_exact(32) + .map(|c| Address::from_slice(c).expect("malformed address chunk")) + } + /// Build a deterministic, collision-resistant `Name` for this address: /// `Ix._#.`. Mirrors Lean-side `Ix.Address.toUniqueName`. /// @@ -43,8 +60,8 @@ impl Address { /// name that can't collide with any Lean-originated name (e.g. for /// scratch `KEnv` entries that should not participate in the /// `name_to_addr` / `aux_name_to_addr` namespace). - pub fn to_unique_name(&self) -> crate::ix::env::Name { - use crate::ix::env::Name; + pub fn to_unique_name(&self) -> crate::env::Name { + use crate::env::Name; Name::str( Name::str(Name::str(Name::anon(), "Ix".to_string()), "_#".to_string()), self.hex(), @@ -53,8 +70,8 @@ impl Address { /// Inverse of `to_unique_name`. Returns `Some(Address)` iff `name` has /// shape `Ix._#.` with valid 64-char hex; otherwise `None`. - pub fn from_unique_name(name: &crate::ix::env::Name) -> Option { - use crate::ix::env::NameData; + pub fn from_unique_name(name: &crate::env::Name) -> Option { + use crate::env::NameData; let (parent, hex) = match name.as_data() { NameData::Str(parent, s, _) => (parent.clone(), s.clone()), _ => return None, @@ -79,11 +96,8 @@ impl Address { /// Used by `compile/mutual.rs` to register each mutual block under a /// Muts-tagged meta so kernel ingress can discover and process it via /// `ingress_muts_block`. - pub fn muts_name( - &self, - first_member: &crate::ix::env::Name, - ) -> crate::ix::env::Name { - use crate::ix::env::{Name, NameData}; + pub fn muts_name(&self, first_member: &crate::env::Name) -> crate::env::Name { + use crate::env::{Name, NameData}; let base = Name::str(Name::str(Name::anon(), "Ix".to_string()), self.hex()); // Append each component of `first_member` to the base, preserving // numeric vs string parts. @@ -127,8 +141,8 @@ impl StdHash for Address { } } -#[cfg(test)] -pub mod tests { +#[cfg(any(test, feature = "quickcheck"))] +pub mod arbitrary { use super::*; use quickcheck::{Arbitrary, Gen}; diff --git a/src/ix/env.rs b/crates/common/src/env.rs similarity index 97% rename from src/ix/env.rs rename to crates/common/src/env.rs index ca7000568..b2d6675a6 100644 --- a/src/ix/env.rs +++ b/crates/common/src/env.rs @@ -14,7 +14,7 @@ use std::{ sync::Arc, }; -use lean_ffi::nat::Nat; +use bignat::Nat; use rustc_hash::FxHashMap; // -- Name tags ---------------------------------------------------------------- @@ -1464,3 +1464,51 @@ impl ConstantInfo { /// The Lean kernel environment: a map from names to their constant declarations. pub type Env = FxHashMap; + +#[cfg(any(test, feature = "quickcheck"))] +pub mod arbitrary { + use super::*; + use quickcheck::{Arbitrary, Gen}; + + impl Arbitrary for DefinitionSafety { + fn arbitrary(g: &mut Gen) -> Self { + match u8::arbitrary(g) % 3 { + 0 => DefinitionSafety::Unsafe, + 1 => DefinitionSafety::Safe, + _ => DefinitionSafety::Partial, + } + } + } + + impl Arbitrary for QuotKind { + fn arbitrary(g: &mut Gen) -> Self { + match u8::arbitrary(g) % 4 { + 0 => QuotKind::Type, + 1 => QuotKind::Ctor, + 2 => QuotKind::Lift, + _ => QuotKind::Ind, + } + } + } + + impl Arbitrary for BinderInfo { + fn arbitrary(g: &mut Gen) -> Self { + match u8::arbitrary(g) % 4 { + 0 => Self::Default, + 1 => Self::Implicit, + 2 => Self::StrictImplicit, + _ => Self::InstImplicit, + } + } + } + + impl Arbitrary for ReducibilityHints { + fn arbitrary(g: &mut Gen) -> Self { + match u8::arbitrary(g) % 3 { + 0 => Self::Opaque, + 1 => Self::Abbrev, + _ => Self::Regular(u32::arbitrary(g)), + } + } + } +} diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs new file mode 100644 index 000000000..e51d043cf --- /dev/null +++ b/crates/common/src/lib.rs @@ -0,0 +1,21 @@ +//! Foundational types shared across the Ix project. +//! +//! Contains: +//! - `address`: 32-byte content-address newtype. +//! - `env`: the Lean kernel type representation (`Name`, `Level`, `Expr`, +//! `ConstantInfo`, `Env`, …) and the tag-byte constants used throughout. +//! - `mutual`: mutual-block helpers used by both compile and kernel. +//! - `strong_ordering`: small comparison helper. +//! +//! Plus the `FxIndexMap` / `FxIndexSet` aliases that the rest of the project +//! uses. + +use indexmap::{IndexMap, IndexSet}; +use rustc_hash::FxBuildHasher; + +pub mod address; +pub mod env; +pub mod strong_ordering; + +pub type FxIndexMap = IndexMap; +pub type FxIndexSet = IndexSet; diff --git a/src/ix/strong_ordering.rs b/crates/common/src/strong_ordering.rs similarity index 100% rename from src/ix/strong_ordering.rs rename to crates/common/src/strong_ordering.rs diff --git a/crates/compile/Cargo.toml b/crates/compile/Cargo.toml new file mode 100644 index 000000000..3fff593a0 --- /dev/null +++ b/crates/compile/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "ix-compile" +version.workspace = true +edition.workspace = true +license.workspace = true + +[dependencies] +anyhow = { workspace = true } +bignat = { workspace = true } +blake3 = { workspace = true } +dashmap = { workspace = true, features = ["rayon"] } +indexmap = { workspace = true, features = ["rayon"] } +itertools = { workspace = true } +ix-common = { workspace = true } +ixon = { workspace = true } +ix-kernel = { workspace = true } +num-bigint = { workspace = true } +rayon = { workspace = true } +rustc-hash = { workspace = true } +sha2 = { workspace = true } + +[lints] +workspace = true diff --git a/src/ix/compile.rs b/crates/compile/src/compile.rs similarity index 97% rename from src/ix/compile.rs rename to crates/compile/src/compile.rs index a16b2bc1b..07949d777 100644 --- a/src/ix/compile.rs +++ b/crates/compile/src/compile.rs @@ -13,36 +13,38 @@ use std::{ sync::{Arc, atomic::Ordering as AtomicOrdering}, }; -use lean_ffi::nat::Nat; - -use crate::{ - ix::address::Address, - ix::env::{ - AxiomVal, BinderInfo, ConstantInfo as LeanConstantInfo, ConstructorVal, - DataValue as LeanDataValue, Env as LeanEnv, Expr as LeanExpr, ExprData, - InductiveVal, Level, LevelData, Literal, Name, NameData, QuotVal, - RecursorRule as LeanRecursorRule, SourceInfo as LeanSourceInfo, - Substring as LeanSubstring, Syntax as LeanSyntax, SyntaxPreresolved, +use bignat::Nat; + +use ix_common::address::Address; +use ix_common::env::{ + AxiomVal, BinderInfo, ConstantInfo as LeanConstantInfo, ConstructorVal, + DataValue as LeanDataValue, Env as LeanEnv, Expr as LeanExpr, ExprData, + InductiveVal, Level, LevelData, Literal, Name, NameData, QuotVal, + RecursorRule as LeanRecursorRule, SourceInfo as LeanSourceInfo, + Substring as LeanSubstring, Syntax as LeanSyntax, SyntaxPreresolved, +}; +use ix_common::strong_ordering::SOrd; + +use ixon::{ + CompileError, Tag0, + constant::{ + Axiom, Constant, ConstantInfo, Constructor, Definition, Inductive, + MutConst as IxonMutConst, Quotient, Recursor, RecursorRule, + ctor_proj_constant, defn_proj_constant, indc_proj_constant, + recr_proj_constant, }, - ix::graph::NameSet, - ix::ixon::{ - CompileError, Tag0, - constant::{ - Axiom, Constant, ConstantInfo, Constructor, Definition, Inductive, - MutConst as IxonMutConst, Quotient, Recursor, RecursorRule, - ctor_proj_constant, defn_proj_constant, indc_proj_constant, - recr_proj_constant, - }, - env::{Env as IxonEnv, Named}, - expr::Expr, - metadata::{ - ConstantMeta, ConstantMetaInfo, DataValue, ExprMeta, ExprMetaData, KVMap, - }, - sharing::{self, analyze_block, build_sharing_vec, decide_sharing}, - univ::Univ, + env::{Env as IxonEnv, Named}, + expr::Expr, + metadata::{ + ConstantMeta, ConstantMetaInfo, DataValue, ExprMeta, ExprMetaData, KVMap, }, - ix::mutual::{Def, Ind, MutConst, MutCtx, Rec, ctx_to_all}, - ix::strong_ordering::SOrd, + sharing::{self, analyze_block, build_sharing_vec, decide_sharing}, + univ::Univ, +}; + +use crate::{ + graph::NameSet, + mutual::{Def, Ind, MutConst, MutCtx, Rec, ctx_to_all}, }; /// Whether to track hash-consed sizes during compilation. @@ -87,7 +89,7 @@ pub struct KernelCtx { /// etc.) with aux-substituted types at `resolve_lean_name_addr`-derived /// addresses that may shift as alpha-collapse reassigns addresses over /// the course of compilation. - pub kenv: crate::ix::kernel::env::KEnv, + pub kenv: ix_kernel::env::KEnv, } impl Default for KernelCtx { @@ -98,7 +100,7 @@ impl Default for KernelCtx { impl KernelCtx { pub fn new() -> Self { - KernelCtx { kenv: crate::ix::kernel::env::KEnv::new() } + KernelCtx { kenv: ix_kernel::env::KEnv::new() } } } @@ -442,7 +444,7 @@ fn compile_univ_indices( fn univ_sort_key(univ: &Arc) -> Vec { let mut buf = Vec::new(); - crate::ix::ixon::univ::put_univ(univ, &mut buf); + ixon::univ::put_univ(univ, &mut buf); buf } @@ -540,7 +542,7 @@ fn collect_expr_tables( Ok(()) } -pub(crate) fn preseed_expr_tables( +pub fn preseed_expr_tables( exprs: &[(&LeanExpr, &[Name])], mut_ctx: &MutCtx, cache: &mut BlockCache, @@ -582,7 +584,7 @@ pub(crate) fn preseed_expr_tables( Ok(()) } -pub(crate) fn collect_mut_const_exprs<'a>( +pub fn collect_mut_const_exprs<'a>( cnst: &'a MutConst, exprs: &mut Vec<(&'a LeanExpr, &'a [Name])>, ) { @@ -621,7 +623,7 @@ pub fn compile_expr( cache: &mut BlockCache, stt: &CompileState, ) -> Result, CompileError> { - use crate::ix::ixon::metadata::CallSiteEntry; + use ixon::metadata::CallSiteEntry; // Stack-based iterative compilation to avoid stack overflow enum Frame { @@ -774,7 +776,7 @@ pub fn compile_expr( let compiling_is_aux_regen = cache .compiling .as_ref() - .is_some_and(crate::ix::decompile::is_aux_gen_suffix); + .is_some_and(crate::decompile::is_aux_gen_suffix); if !compiling_is_aux_regen { if let Some(plan) = stt.call_site_plans.get(name) && !plan.is_identity() @@ -1461,11 +1463,11 @@ fn compile_data_value(dv: &LeanDataValue, stt: &CompileState) -> DataValue { // Serialize Int and store as blob let mut bytes = Vec::new(); match i { - crate::ix::env::Int::OfNat(n) => { + ix_common::env::Int::OfNat(n) => { bytes.push(0); bytes.extend_from_slice(&n.to_le_bytes()); }, - crate::ix::env::Int::NegSucc(n) => { + ix_common::env::Int::NegSucc(n) => { bytes.push(1); bytes.extend_from_slice(&n.to_le_bytes()); }, @@ -1671,16 +1673,16 @@ fn apply_sharing(exprs: Vec>) -> (Vec>, Vec>) { } /// Result of applying sharing to a singleton constant. -pub(crate) struct SingletonSharingResult { +pub struct SingletonSharingResult { /// The compiled Constant - pub(crate) constant: Constant, + pub constant: Constant, /// Hash-consed size of expressions - pub(crate) hash_consed_size: usize, + pub hash_consed_size: usize, } /// Apply sharing to a Definition and return a Constant with stats. #[allow(clippy::needless_pass_by_value)] -pub(crate) fn apply_sharing_to_definition_with_stats( +pub fn apply_sharing_to_definition_with_stats( def: Definition, refs: Vec
, univs: Vec>, @@ -1704,7 +1706,7 @@ pub(crate) fn apply_sharing_to_definition_with_stats( /// Apply sharing to an Axiom and return a Constant with stats. #[allow(clippy::needless_pass_by_value)] -pub(crate) fn apply_sharing_to_axiom_with_stats( +pub fn apply_sharing_to_axiom_with_stats( ax: Axiom, refs: Vec
, univs: Vec>, @@ -1722,7 +1724,7 @@ pub(crate) fn apply_sharing_to_axiom_with_stats( /// Apply sharing to a Quotient and return a Constant with stats. #[allow(clippy::needless_pass_by_value)] -pub(crate) fn apply_sharing_to_quotient_with_stats( +pub fn apply_sharing_to_quotient_with_stats( quot: Quotient, refs: Vec
, univs: Vec>, @@ -1743,7 +1745,7 @@ pub(crate) fn apply_sharing_to_quotient_with_stats( } /// Apply sharing to a Recursor and return a Constant with stats. -pub(crate) fn apply_sharing_to_recursor_with_stats( +pub fn apply_sharing_to_recursor_with_stats( rec: Recursor, refs: Vec
, univs: Vec>, @@ -1780,15 +1782,15 @@ pub(crate) fn apply_sharing_to_recursor_with_stats( } /// Result of applying sharing to a mutual block. -pub(crate) struct MutualBlockSharingResult { +pub struct MutualBlockSharingResult { /// The compiled Constant - pub(crate) constant: Constant, + pub constant: Constant, /// Hash-consed size of all expressions in the block - pub(crate) hash_consed_size: usize, + pub hash_consed_size: usize, } /// Apply sharing to a mutual block and return a Constant with stats. -pub(crate) fn apply_sharing_to_mutual_block( +pub fn apply_sharing_to_mutual_block( mut_consts: Vec, refs: Vec
, univs: Vec>, @@ -1960,7 +1962,7 @@ enum MutConstKind { /// Compile a Definition. /// Arena persists across type + value within a constant. -pub(crate) fn compile_definition( +pub fn compile_definition( def: &Def, mut_ctx: &MutCtx, cache: &mut BlockCache, @@ -2031,7 +2033,7 @@ fn compile_recursor_rule( /// Compile a Recursor. /// Arena grows across type and all rule RHS expressions. -pub(crate) fn compile_recursor( +pub fn compile_recursor( rec: &Rec, mut_ctx: &MutCtx, cache: &mut BlockCache, @@ -2158,7 +2160,7 @@ fn compile_constructor( /// The inductive type gets its own arena. Each constructor gets its own arena /// via compile_constructor. No CtorMeta duplication — ConstantMeta::Indc only /// stores constructor name addresses. -pub(crate) fn compile_inductive( +pub fn compile_inductive( ind: &Ind, mut_ctx: &MutCtx, cache: &mut BlockCache, @@ -2315,20 +2317,20 @@ fn compile_quotient( // =========================================================================== /// Result of compiling a mutual block. -pub(crate) struct CompiledMutualBlock { +pub struct CompiledMutualBlock { /// The compiled Constant - pub(crate) constant: Constant, + pub constant: Constant, /// Content-addressed hash - pub(crate) addr: Address, + pub addr: Address, /// Hash-consed size (theoretical minimum with perfect DAG sharing) - pub(crate) hash_consed_size: usize, + pub hash_consed_size: usize, /// Serialized size (actual bytes) - pub(crate) serialized_size: usize, + pub serialized_size: usize, } /// Compile a mutual block with block-level sharing. /// Returns the Constant, its content-addressed hash, and size statistics. -pub(crate) fn compile_mutual_block( +pub fn compile_mutual_block( mut_consts: Vec, refs: Vec
, univs: Vec>, @@ -3890,18 +3892,18 @@ fn compile_mutual( .map(|r| r.clone()) } -pub(crate) mod aux_gen; +pub mod aux_gen; mod env; -pub(crate) mod mutual; -pub(crate) mod nat_conv; -pub(crate) mod surgery; +pub mod mutual; +pub mod nat_conv; +pub mod surgery; pub use env::{compile_env, compile_env_with_options}; #[cfg(test)] mod tests { use super::*; - use crate::ix::env::{BinderInfo, Expr as LeanExpr, Level}; - use crate::ix::ixon::metadata::CallSiteEntry; + use ix_common::env::{BinderInfo, Expr as LeanExpr, Level}; + use ixon::metadata::CallSiteEntry; #[test] fn test_compile_univ_zero() { @@ -4314,7 +4316,7 @@ mod tests { #[test] fn test_compile_axiom() { - use crate::ix::env::{AxiomVal, ConstantVal}; + use ix_common::env::{AxiomVal, ConstantVal}; // Create a simple axiom: axiom myAxiom : Type let name = Name::str(Name::anon(), "myAxiom".to_string()); @@ -4348,7 +4350,7 @@ mod tests { #[test] fn test_compile_simple_def() { - use crate::ix::env::{ + use ix_common::env::{ ConstantVal, DefinitionSafety, DefinitionVal, ReducibilityHints, }; @@ -4402,11 +4404,11 @@ mod tests { #[test] fn test_compile_self_referential_def() { - use crate::ix::env::{ + use ix_common::env::{ ConstantInfo as LeanConstantInfo, ConstantVal, DefinitionSafety, DefinitionVal, Env as LeanEnv, ReducibilityHints, }; - use crate::ix::ixon::constant::ConstantInfo; + use ixon::constant::ConstantInfo; // Create a self-referential definition (like a recursive function placeholder) // def myDef : Type := myDef (this is silly but tests the mutual handling) @@ -4462,7 +4464,7 @@ mod tests { #[test] fn test_compile_env_single_axiom() { - use crate::ix::env::{AxiomVal, ConstantVal}; + use ix_common::env::{AxiomVal, ConstantVal}; // Create a minimal environment with just one axiom let name = Name::str(Name::anon(), "myAxiom".to_string()); @@ -4484,7 +4486,7 @@ mod tests { #[test] fn test_compile_env_two_independent_axioms() { - use crate::ix::env::{AxiomVal, ConstantVal}; + use ix_common::env::{AxiomVal, ConstantVal}; let name1 = Name::str(Name::anon(), "axiom1".to_string()); let name2 = Name::str(Name::anon(), "axiom2".to_string()); @@ -4528,7 +4530,7 @@ mod tests { #[test] fn test_compile_env_def_referencing_axiom() { - use crate::ix::env::{ + use ix_common::env::{ AxiomVal, ConstantVal, DefinitionSafety, DefinitionVal, ReducibilityHints, }; @@ -4578,7 +4580,7 @@ mod tests { /// to the single representative in the Muts array. #[test] fn test_compile_mutual_alpha_equivalent_defs() { - use crate::ix::env::{ + use ix_common::env::{ ConstantVal, DefinitionSafety, DefinitionVal, ReducibilityHints, }; @@ -4656,7 +4658,7 @@ mod tests { /// with projections indexing correctly into the array. #[test] fn test_compile_mutual_alpha_equiv_with_different_third() { - use crate::ix::env::{ + use ix_common::env::{ ConstantVal, DefinitionSafety, DefinitionVal, ReducibilityHints, }; @@ -4765,8 +4767,8 @@ mod tests { #[test] fn test_mutual_block_roundtrip() { - use crate::ix::env::DefinitionSafety; - use crate::ix::ixon::constant::{DefKind, Definition}; + use ix_common::env::DefinitionSafety; + use ixon::constant::{DefKind, Definition}; // Create a mutual block and verify it roundtrips through serialization let sort0 = Expr::sort(0); @@ -4836,7 +4838,7 @@ mod tests { #[test] fn test_definition_with_sharing() { - use crate::ix::ixon::constant::{DefKind, Definition}; + use ixon::constant::{DefKind, Definition}; // Create a definition where typ and value share structure let sort0 = Expr::sort(0); @@ -4857,7 +4859,7 @@ mod tests { // Create constant with sharing at Constant level let def = Definition { kind: DefKind::Definition, - safety: crate::ix::env::DefinitionSafety::Safe, + safety: ix_common::env::DefinitionSafety::Safe, lvls: 0, typ: rewritten[0].clone(), value: rewritten[1].clone(), @@ -4880,7 +4882,7 @@ mod tests { #[test] fn test_axiom_with_sharing() { - use crate::ix::ixon::constant::Axiom; + use ixon::constant::Axiom; // Axiom with repeated subterms in its type let sort0 = Expr::sort(0); @@ -4915,7 +4917,7 @@ mod tests { #[test] fn test_recursor_with_sharing() { - use crate::ix::ixon::constant::{Recursor, RecursorRule}; + use ixon::constant::{Recursor, RecursorRule}; // Recursor with shared subterms across typ and rules let sort0 = Expr::sort(0); @@ -4981,7 +4983,7 @@ mod tests { #[test] fn test_inductive_with_sharing() { - use crate::ix::ixon::constant::{Constructor, Inductive}; + use ixon::constant::{Constructor, Inductive}; // Inductive with shared subterms across type and constructors let sort0 = Expr::sort(0); @@ -5091,8 +5093,8 @@ mod tests { #[test] fn test_roundtrip_axiom() { - use crate::ix::decompile::decompile_env; - use crate::ix::env::{AxiomVal, ConstantVal}; + use crate::decompile::decompile_env; + use ix_common::env::{AxiomVal, ConstantVal}; // Create an axiom: axiom myAxiom : Type let name = Name::str(Name::anon(), "myAxiom".to_string()); @@ -5125,8 +5127,8 @@ mod tests { #[test] fn test_roundtrip_axiom_with_level_params() { - use crate::ix::decompile::decompile_env; - use crate::ix::env::{AxiomVal, ConstantVal, Env as LeanEnv}; + use crate::decompile::decompile_env; + use ix_common::env::{AxiomVal, ConstantVal, Env as LeanEnv}; // Create an axiom with universe params: axiom myAxiom.{u, v} : Sort (max u v) let name = Name::str(Name::anon(), "myAxiom".to_string()); @@ -5168,8 +5170,8 @@ mod tests { #[test] fn test_roundtrip_definition() { - use crate::ix::decompile::decompile_env; - use crate::ix::env::{ + use crate::decompile::decompile_env; + use ix_common::env::{ ConstantVal, DefinitionSafety, DefinitionVal, ReducibilityHints, }; @@ -5180,13 +5182,13 @@ mod tests { Name::str(Name::anon(), "x".to_string()), type1.clone(), type1.clone(), - crate::ix::env::BinderInfo::Default, + ix_common::env::BinderInfo::Default, ); let value = LeanExpr::lam( Name::str(Name::anon(), "x".to_string()), type1, LeanExpr::bvar(Nat::from(0u64)), - crate::ix::env::BinderInfo::Default, + ix_common::env::BinderInfo::Default, ); let def = DefinitionVal { cnst: ConstantVal { name: name.clone(), level_params: vec![], typ }, @@ -5221,8 +5223,8 @@ mod tests { #[test] fn test_roundtrip_def_referencing_axiom() { - use crate::ix::decompile::decompile_env; - use crate::ix::env::{ + use crate::decompile::decompile_env; + use ix_common::env::{ AxiomVal, ConstantVal, DefinitionSafety, DefinitionVal, Env as LeanEnv, ReducibilityHints, }; @@ -5278,8 +5280,8 @@ mod tests { #[test] fn test_roundtrip_quotient() { - use crate::ix::decompile::decompile_env; - use crate::ix::env::{ConstantVal, Env as LeanEnv, QuotKind, QuotVal}; + use crate::decompile::decompile_env; + use ix_common::env::{ConstantVal, Env as LeanEnv, QuotKind, QuotVal}; // Create quotient constants let quot_name = Name::str(Name::anon(), "Quot".to_string()); @@ -5298,9 +5300,9 @@ mod tests { Name::anon(), LeanExpr::bvar(Nat::from(1u64)), prop.clone(), - crate::ix::env::BinderInfo::Default, + ix_common::env::BinderInfo::Default, ), - crate::ix::env::BinderInfo::Default, + ix_common::env::BinderInfo::Default, ); let typ = LeanExpr::all( alpha, @@ -5309,9 +5311,9 @@ mod tests { Name::anon(), rel_type, sort_u.clone(), - crate::ix::env::BinderInfo::Default, + ix_common::env::BinderInfo::Default, ), - crate::ix::env::BinderInfo::Default, + ix_common::env::BinderInfo::Default, ); let quot = QuotVal { @@ -5348,8 +5350,8 @@ mod tests { #[test] fn test_roundtrip_theorem() { - use crate::ix::decompile::decompile_env; - use crate::ix::env::{ConstantVal, Env as LeanEnv, TheoremVal}; + use crate::decompile::decompile_env; + use ix_common::env::{ConstantVal, Env as LeanEnv, TheoremVal}; // Create a theorem: theorem trivial : True := True.intro let name = Name::str(Name::anon(), "trivial".to_string()); @@ -5389,8 +5391,8 @@ mod tests { #[test] fn test_roundtrip_opaque() { - use crate::ix::decompile::decompile_env; - use crate::ix::env::{ConstantVal, Env as LeanEnv, OpaqueVal}; + use crate::decompile::decompile_env; + use ix_common::env::{ConstantVal, Env as LeanEnv, OpaqueVal}; // Create an opaque: opaque secret : Nat := 42 let name = Name::str(Name::anon(), "secret".to_string()); @@ -5431,8 +5433,8 @@ mod tests { #[test] fn test_roundtrip_multiple_constants() { - use crate::ix::decompile::decompile_env; - use crate::ix::env::{ + use crate::decompile::decompile_env; + use ix_common::env::{ AxiomVal, ConstantVal, DefinitionSafety, DefinitionVal, Env as LeanEnv, ReducibilityHints, TheoremVal, }; @@ -5506,8 +5508,8 @@ mod tests { #[test] fn test_roundtrip_inductive_simple() { - use crate::ix::decompile::decompile_env; - use crate::ix::env::{ + use crate::decompile::decompile_env; + use ix_common::env::{ ConstantVal, ConstructorVal, Env as LeanEnv, InductiveVal, }; @@ -5589,8 +5591,8 @@ mod tests { #[test] fn test_roundtrip_inductive_with_multiple_ctors() { - use crate::ix::decompile::decompile_env; - use crate::ix::env::{ + use crate::decompile::decompile_env; + use ix_common::env::{ ConstantVal, ConstructorVal, Env as LeanEnv, InductiveVal, }; @@ -5673,8 +5675,8 @@ mod tests { #[test] fn test_roundtrip_mutual_definitions() { - use crate::ix::decompile::decompile_env; - use crate::ix::env::{ + use crate::decompile::decompile_env; + use ix_common::env::{ ConstantVal, DefinitionSafety, DefinitionVal, Env as LeanEnv, ReducibilityHints, }; @@ -5690,7 +5692,7 @@ mod tests { Name::anon(), type0.clone(), type0.clone(), - crate::ix::env::BinderInfo::Default, + ix_common::env::BinderInfo::Default, ); // f := fun x => g x @@ -5701,7 +5703,7 @@ mod tests { LeanExpr::cnst(g_name.clone(), vec![]), LeanExpr::bvar(Nat::from(0u64)), ), - crate::ix::env::BinderInfo::Default, + ix_common::env::BinderInfo::Default, ); // g := fun x => f x @@ -5712,7 +5714,7 @@ mod tests { LeanExpr::cnst(f_name.clone(), vec![]), LeanExpr::bvar(Nat::from(0u64)), ), - crate::ix::env::BinderInfo::Default, + ix_common::env::BinderInfo::Default, ); // Mutual block: both reference each other @@ -5784,8 +5786,8 @@ mod tests { #[test] fn test_roundtrip_mutual_inductives() { - use crate::ix::decompile::decompile_env; - use crate::ix::env::{ + use crate::decompile::decompile_env; + use ix_common::env::{ ConstantVal, ConstructorVal, Env as LeanEnv, InductiveVal, }; @@ -5855,7 +5857,7 @@ mod tests { Name::anon(), odd_type.clone(), even_type.clone(), - crate::ix::env::BinderInfo::Default, + ix_common::env::BinderInfo::Default, ); let even_succ_ctor = ConstructorVal { @@ -5876,7 +5878,7 @@ mod tests { Name::anon(), even_type.clone(), odd_type.clone(), - crate::ix::env::BinderInfo::Default, + ix_common::env::BinderInfo::Default, ); let odd_succ_ctor = ConstructorVal { @@ -5942,8 +5944,8 @@ mod tests { #[test] fn test_roundtrip_inductive_with_recursor() { - use crate::ix::decompile::decompile_env; - use crate::ix::env::{ConstantVal, InductiveVal, RecursorVal}; + use crate::decompile::decompile_env; + use ix_common::env::{ConstantVal, InductiveVal, RecursorVal}; // Create Empty type with recursor (no constructors) // inductive Empty : Type @@ -5976,7 +5978,7 @@ mod tests { Name::anon(), empty_type.clone(), LeanExpr::sort(Level::param(u.clone())), - crate::ix::env::BinderInfo::Default, + ix_common::env::BinderInfo::Default, ); let rec_type = LeanExpr::all( Name::str(Name::anon(), "motive".to_string()), @@ -5988,9 +5990,9 @@ mod tests { LeanExpr::bvar(Nat::from(1u64)), LeanExpr::bvar(Nat::from(0u64)), ), - crate::ix::env::BinderInfo::Default, + ix_common::env::BinderInfo::Default, ), - crate::ix::env::BinderInfo::Implicit, + ix_common::env::BinderInfo::Implicit, ); let recursor = RecursorVal { diff --git a/src/ix/compile/aux_gen.rs b/crates/compile/src/compile/aux_gen.rs similarity index 96% rename from src/ix/compile/aux_gen.rs rename to crates/compile/src/compile/aux_gen.rs index 080a57e8c..f9e06e5ac 100644 --- a/src/ix/compile/aux_gen.rs +++ b/crates/compile/src/compile/aux_gen.rs @@ -74,28 +74,28 @@ //! - `.sparseCasesOn`, `.sparseCasesOnEq` //! - `.casesOnSameCtor`, `.casesOnSameCtorHet` -pub(crate) mod below; -pub(crate) mod brecon; -pub(crate) mod cases_on; -pub(crate) mod expr_utils; -pub(crate) mod nested; -pub(crate) mod rec_on; -pub(crate) mod recursor; +pub mod below; +pub mod brecon; +pub mod cases_on; +pub mod expr_utils; +pub mod nested; +pub mod rec_on; +pub mod recursor; use std::sync::Arc; use rustc_hash::FxHashMap; -use crate::ix::compile::CompileState; -use crate::ix::env::{ +use crate::compile::CompileState; +use ix_common::env::{ ConstantVal, Env as LeanEnv, Expr as LeanExpr, Name, RecursorRule, RecursorVal, }; -use crate::ix::ixon::CompileError; +use ixon::CompileError; /// A regenerated constant ready for compilation. #[derive(Clone)] -pub(crate) enum PatchedConstant { +pub enum PatchedConstant { /// A regenerated `.rec` recursor. Rec(RecursorVal), /// A regenerated `.recOn` definition (arg-reordered `.rec` wrapper). @@ -118,7 +118,7 @@ pub(crate) enum PatchedConstant { /// flips to `Unsafe` whenever the type or value mentions any unsafe constant — /// and every auxiliary references its parent inductive. #[derive(Clone)] -pub(crate) struct AuxDef { +pub struct AuxDef { pub name: Name, pub level_params: Vec, pub typ: LeanExpr, @@ -134,7 +134,7 @@ pub(crate) struct AuxDef { /// (to canonicalize Lean-source-order originals before structural /// comparison). #[derive(Clone, Default)] -pub(crate) struct AuxPatchesOutput { +pub struct AuxPatchesOutput { /// The regenerated canonical-layout constants, keyed by their /// Lean-visible source-indexed name (e.g. `A.rec`, `A.below_2`). pub patches: FxHashMap, @@ -185,12 +185,12 @@ pub(crate) struct AuxPatchesOutput { /// `InductiveVal.all` of any block member). It determines the canonical /// `.rec_N` naming and the source-aux walk used to compute the /// hash-sort permutation. -pub(crate) fn generate_aux_patches( +pub fn generate_aux_patches( sorted_classes: &[Vec], original_all: &[Name], lean_env: &Arc, stt: &CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + kctx: &mut crate::compile::KernelCtx, ) -> Result { let mut patches: FxHashMap = FxHashMap::default(); let mut aliases: FxHashMap = FxHashMap::default(); @@ -257,8 +257,8 @@ pub(crate) fn generate_aux_patches( let metadata_has_nested = original_all.iter().any(|name| { matches!( lean_env.get(name), - Some(crate::ix::env::ConstantInfo::InductInfo(v)) - if crate::ix::compile::nat_conv::nat_to_usize(&v.num_nested) > 0 + Some(ix_common::env::ConstantInfo::InductInfo(v)) + if crate::compile::nat_conv::nat_to_usize(&v.num_nested) > 0 ) }); let (canonical_recs, is_prop) = if metadata_has_nested @@ -529,7 +529,7 @@ pub(crate) fn generate_aux_patches( for (rec_name, rec_val) in canonical_recs.iter().take(n_classes) { // Build casesOn name: rec_name is "I.rec", casesOn name is "I.casesOn" let ind_name = match rec_name.as_data() { - crate::ix::env::NameData::Str(parent, _, _) => parent.clone(), + ix_common::env::NameData::Str(parent, _, _) => parent.clone(), _ => continue, }; let cases_on_name = Name::str(ind_name, "casesOn".to_string()); @@ -549,7 +549,7 @@ pub(crate) fn generate_aux_patches( // types (unlike below_N/brecOn_N which ARE generated via BRecOn.lean). for (rec_name, rec_val) in canonical_recs.iter().take(n_classes) { let ind_name = match rec_name.as_data() { - crate::ix::env::NameData::Str(parent, _, _) => parent.clone(), + ix_common::env::NameData::Str(parent, _, _) => parent.clone(), _ => continue, }; let rec_on_name = Name::str(ind_name, "recOn".to_string()); @@ -650,7 +650,7 @@ pub(crate) fn generate_aux_patches( .and_then(|c| c.first()) .map(|n| n.pretty()) .unwrap_or_default(); - if *crate::ix::compile::IX_TIMING + if *crate::compile::IX_TIMING && _below_elapsed.as_secs_f32() + _brecon_elapsed.as_secs_f32() > 0.3 { eprintln!( @@ -669,7 +669,7 @@ pub(crate) fn generate_aux_patches( .and_then(|c| c.first()) .map(|n| n.pretty()) .unwrap_or_default(); - if *crate::ix::compile::IX_TIMING && _p1_elapsed.as_secs_f32() > 0.5 { + if *crate::compile::IX_TIMING && _p1_elapsed.as_secs_f32() > 0.5 { eprintln!( "[gen_patches] {:?} recGen={:.2}s patches={}", _gen_label, @@ -721,13 +721,13 @@ pub(crate) fn generate_aux_patches( ) { let rep_ctors = match lean_env.get(rep) { - Some(crate::ix::env::ConstantInfo::InductInfo(v)) => { + Some(ix_common::env::ConstantInfo::InductInfo(v)) => { v.ctors.clone() }, _ => vec![], }; let alias_ctors = match lean_env.get(alias) { - Some(crate::ix::env::ConstantInfo::InductInfo(v)) => { + Some(ix_common::env::ConstantInfo::InductInfo(v)) => { v.ctors.clone() }, _ => vec![], @@ -896,7 +896,7 @@ pub(crate) fn generate_aux_patches( /// This distinguishes `.below` auxiliaries from coincidental name collisions /// like structure field accessors (e.g., `NewDecl.below : NewDecl → LocalDecl`). fn is_below_shaped(typ: &LeanExpr) -> bool { - use crate::ix::env::ExprData; + use ix_common::env::ExprData; let mut cur = typ; loop { match cur.as_data() { @@ -914,16 +914,16 @@ fn is_below_shaped(typ: &LeanExpr) -> bool { /// and may differ from the originals in `lean_env`. The canonical TC /// (`stt.canon_tc`) uses `canon_kenv` exclusively, so it sees the /// correct types for PProd(motive, I.below ...) inference. -pub(crate) fn populate_canon_kenv_with_below( +pub fn populate_canon_kenv_with_below( below_consts: &[below::BelowConstant], sorted_classes: &[Vec], - lean_env: &crate::ix::env::Env, + lean_env: &ix_common::env::Env, stt: &CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + kctx: &mut crate::compile::KernelCtx, ) { - use crate::ix::kernel::constant::KConst; - use crate::ix::kernel::id::KId; - use crate::ix::kernel::ingress::{ + use ix_kernel::constant::KConst; + use ix_kernel::id::KId; + use ix_kernel::ingress::{ lean_expr_to_zexpr_with_kenv, resolve_lean_name_addr, }; @@ -965,9 +965,9 @@ pub(crate) fn populate_canon_kenv_with_below( KConst::Defn { name: d.name.clone(), level_params: d.level_params.clone(), - kind: crate::ix::ixon::constant::DefKind::Definition, - safety: crate::ix::env::DefinitionSafety::Safe, - hints: crate::ix::env::ReducibilityHints::Abbrev, + kind: ixon::constant::DefKind::Definition, + safety: ix_common::env::DefinitionSafety::Safe, + hints: ix_common::env::ReducibilityHints::Abbrev, lvls: d.level_params.len() as u64, ty: ty_z, val: val_z, diff --git a/src/ix/compile/aux_gen/below.rs b/crates/compile/src/compile/aux_gen/below.rs similarity index 98% rename from src/ix/compile/aux_gen/below.rs rename to crates/compile/src/compile/aux_gen/below.rs index a483e83f8..407dc5e7d 100644 --- a/src/ix/compile/aux_gen/below.rs +++ b/crates/compile/src/compile/aux_gen/below.rs @@ -8,12 +8,12 @@ //! //! Follows `refs/lean4/src/Lean/Meta/Constructions/BRecOn.lean:59-108`. -use crate::ix::compile::nat_conv::{nat_to_usize, try_nat_to_usize}; -use crate::ix::env::{ +use crate::compile::nat_conv::{nat_to_usize, try_nat_to_usize}; +use ix_common::env::{ BinderInfo, ConstantInfo, ConstructorVal, Env as LeanEnv, Expr as LeanExpr, ExprData, InductiveVal, Level, LevelData, Name, RecursorVal, }; -use crate::ix::ixon::CompileError; +use ixon::CompileError; use super::expr_utils::{ LocalDecl, decompose_apps, find_motive_fvar, forall_telescope, fresh_fvar, @@ -38,7 +38,7 @@ pub(super) fn aux_rec_suffix_idx(aux_rec_name: &Name) -> Option { /// A generated `.below` constant — either a definition (Type-level) /// or an inductive (Prop-level). #[derive(Clone)] -pub(crate) enum BelowConstant { +pub enum BelowConstant { /// Type-level `.below`: a reducible definition using `.rec` + PProd. Def(BelowDef), /// Prop-level `.below`: an inductive type with constructors. @@ -53,7 +53,7 @@ pub(crate) enum BelowConstant { /// type or value references an unsafe constant — for unsafe inductives this /// always triggers because `.below` mentions the parent inductive's `.rec`. #[derive(Clone)] -pub(crate) struct BelowDef { +pub struct BelowDef { pub name: Name, pub level_params: Vec, pub typ: LeanExpr, @@ -63,7 +63,7 @@ pub(crate) struct BelowDef { /// A generated `.below` inductive (Prop-level case). #[derive(Clone)] -pub(crate) struct BelowIndc { +pub struct BelowIndc { pub name: Name, pub level_params: Vec, pub n_params: usize, @@ -87,7 +87,7 @@ pub(crate) struct BelowIndc { /// A constructor for a Prop-level `.below` inductive. #[derive(Clone)] -pub(crate) struct BelowCtor { +pub struct BelowCtor { pub name: Name, pub typ: LeanExpr, pub n_params: usize, @@ -107,13 +107,13 @@ pub(crate) struct BelowCtor { /// Note: `is_prop` is distinct from `is_large`. A Prop inductive with single /// constructors and all-Prop fields gets large elimination (`drec`), but Lean /// still generates `.below` as an inductive via `IndPredBelow`. -pub(crate) fn generate_below_constants( +pub fn generate_below_constants( sorted_classes: &[Vec], canonical_recs: &[(Name, RecursorVal)], lean_env: &LeanEnv, is_prop: bool, - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, ) -> Result, CompileError> { let n_classes = sorted_classes.len(); if n_classes == 0 || canonical_recs.is_empty() { @@ -262,7 +262,7 @@ pub(crate) fn generate_below_constants( /// Build a single `.below` definition for a Type-level inductive. /// /// The `.below` definition's value is: -/// ``` +/// ```text /// λ {params} {motives} (indices) (major), /// I.rec.{succ(rlvl), lvls...} params /// (λ (indices) (major), Sort rlvl) -- for each motive @@ -276,8 +276,8 @@ fn build_below_def( lean_env: &LeanEnv, n_classes: usize, canonical_recs: &[(Name, RecursorVal)], - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, ) -> Result { let n_params = try_nat_to_usize(&rec_val.num_params)?; let n_motives = try_nat_to_usize(&rec_val.num_motives)?; @@ -475,8 +475,8 @@ fn build_below_value( rlvl: &Level, _n_classes: usize, _canonical_recs: &[(Name, RecursorVal)], - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, ) -> Result { let n_params = try_nat_to_usize(&rec_val.num_params)?; let n_motives = try_nat_to_usize(&rec_val.num_motives)?; @@ -1687,10 +1687,10 @@ fn mk_imax_aux(l1: &Level, l2: &Level) -> Level { /// Uses raw `Level::succ` / `Level::max` to faithfully preserve the kernel's /// level structure — no distribution of Succ over Max, no subsumption. pub(super) fn kuniv_to_level( - u: &crate::ix::kernel::level::KUniv, + u: &ix_kernel::level::KUniv, param_names: &[Name], ) -> Level { - use crate::ix::kernel::level::UnivData; + use ix_kernel::level::UnivData; match u.data() { UnivData::Zero(_) => Level::zero(), UnivData::Succ(inner, _) => Level::succ(kuniv_to_level(inner, param_names)), @@ -1774,7 +1774,7 @@ pub(super) fn mk_punit_unit(lvl: &Level) -> LeanExpr { /// Used when extracting motive domains from the recursor type for Prop-level /// `.below` inductives. The recursor may have large elimination (extra `u` /// param), but `.below` motives always target Prop. -pub(crate) fn replace_result_sort_with_prop(expr: &LeanExpr) -> LeanExpr { +pub fn replace_result_sort_with_prop(expr: &LeanExpr) -> LeanExpr { match expr.as_data() { ExprData::ForallE(name, dom, body, bi, _) => LeanExpr::all( name.clone(), diff --git a/src/ix/compile/aux_gen/brecon.rs b/crates/compile/src/compile/aux_gen/brecon.rs similarity index 98% rename from src/ix/compile/aux_gen/brecon.rs rename to crates/compile/src/compile/aux_gen/brecon.rs index dff154d81..fdcd0582b 100644 --- a/src/ix/compile/aux_gen/brecon.rs +++ b/crates/compile/src/compile/aux_gen/brecon.rs @@ -8,13 +8,13 @@ //! `.brecOn.go` uses PProd-wrapped motives; `.brecOn` projects first component. //! Reference: `refs/lean4/src/Lean/Meta/Constructions/BRecOn.lean:191-308` -use crate::ix::compile::nat_conv::try_nat_to_usize; -use crate::ix::env::{ +use crate::compile::nat_conv::try_nat_to_usize; +use bignat::Nat; +use ix_common::env::{ BinderInfo, ConstantInfo, Env as LeanEnv, Expr as LeanExpr, ExprData, InductiveVal, Level, LevelData, Name, RecursorVal, }; -use crate::ix::ixon::CompileError; -use lean_ffi::nat::Nat; +use ixon::CompileError; use super::below::{ BelowConstant, mk_level_succ, mk_pprod, mk_pprod_mk, mk_punit_unit, @@ -44,7 +44,7 @@ use rustc_hash::FxHashMap; /// `.brecOn.eq`. `.go` and `.brecOn` are always `Defn`; `.eq` is `Thm` /// (safe) or unsafe `Defn` with `hints := .opaque`. #[derive(Clone)] -pub(crate) struct BRecOnDef { +pub struct BRecOnDef { pub name: Name, pub level_params: Vec, pub typ: LeanExpr, @@ -59,14 +59,14 @@ pub(crate) struct BRecOnDef { /// from Phase 1 and the `.below` constants from Phase 2. /// `is_prop` determines whether to generate Prop-level (single theorem) or /// Type-level (`.brecOn.go` + `.brecOn`) forms. -pub(crate) fn generate_brecon_constants( +pub fn generate_brecon_constants( sorted_classes: &[Vec], canonical_recs: &[(Name, RecursorVal)], below_consts: &[BelowConstant], lean_env: &LeanEnv, is_prop: bool, - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, ) -> Result, CompileError> { let n_classes = sorted_classes.len(); if n_classes == 0 || canonical_recs.is_empty() || below_consts.is_empty() { @@ -623,8 +623,8 @@ fn build_type_brecon_fvar( below_names: &[Name], lean_env: &LeanEnv, n_classes: usize, - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, ) -> Result, CompileError> { // canon_kenv is populated by `populate_canon_kenv_with_below` in // aux_gen.rs between Phase 2 and Phase 3. It contains PUnit, PProd, @@ -1076,16 +1076,15 @@ fn build_type_brecon_fvar( }, ]; - if let Some((eq_typ, eq_val)) = eq_result { - results.push(BRecOnDef { - name: eq_name, - level_params: rec_level_params.clone(), - typ: eq_typ, - value: eq_val, - is_unsafe, - is_prop: false, - }); - } + let (eq_typ, eq_val) = eq_result; + results.push(BRecOnDef { + name: eq_name, + level_params: rec_level_params.clone(), + typ: eq_typ, + value: eq_val, + is_unsafe, + is_prop: false, + }); Ok(results) } @@ -1441,18 +1440,14 @@ fn build_type_brecon_eq_fvar( // `Eq` and `HEq` binders in `motive_wrapped` and // `build_minor_via_cases_sim`'s remaining list. rec_level_params: &[Name], - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, -) -> Option<(LeanExpr, LeanExpr)> { - // .brecOn.eq requires Eq and Eq.refl as constants. In the full pipeline, - // aux_gen is only called when the original Lean environment has these - // constants, so this always succeeds. But in minimal test environments - // (e.g., unit tests with synthetic inductives), Eq may not exist. - // Return None in that case — matching the old BVar code's behavior. - // - // TODO: Accept a lean_env parameter and check lean_env.get("Eq").is_some() - // for a more principled guard. For now, we always generate .eq since the - // real pipeline guarantees Eq exists. + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, +) -> (LeanExpr, LeanExpr) { + // .brecOn.eq requires Eq and Eq.refl as constants. The real pipeline only + // calls aux_gen when the original Lean environment has these, so this + // always succeeds. If a future minimal-test caller needs to opt out (e.g. + // a synthetic inductive without Eq), reintroduce an `Option` return or + // gate via a `lean_env.get("Eq").is_some()` check. let _ = n_minors; let _n_motives = motive_fvars.len(); @@ -1577,7 +1572,7 @@ fn build_type_brecon_eq_fvar( kctx, ); if let Some(eq_value) = eq_value_opt { - return Some((eq_type, eq_value)); + return (eq_type, eq_value); } // Fall through to the simple path if the indexed construction // couldn't be completed (e.g., missing ctor info). @@ -1717,7 +1712,7 @@ fn build_type_brecon_eq_fvar( let eq_value = mk_lambda(eq_val, all_decls); - Some((eq_type, eq_value)) + (eq_type, eq_value) } // ========================================================================= @@ -1785,8 +1780,8 @@ fn build_indexed_eq_value( // `Eq` and `HEq` binders (matching Lean's `mkEqAndProof` in // `refs/lean4/src/Lean/Meta/Tactic/Cases.lean:30-37`). rec_level_params: &[Name], - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, ) -> Option { let n_indices = index_decls.len(); let outer_major = &major_fvars[0]; diff --git a/src/ix/compile/aux_gen/cases_on.rs b/crates/compile/src/compile/aux_gen/cases_on.rs similarity index 98% rename from src/ix/compile/aux_gen/cases_on.rs rename to crates/compile/src/compile/aux_gen/cases_on.rs index 4d2ec2e76..b14a41638 100644 --- a/src/ix/compile/aux_gen/cases_on.rs +++ b/crates/compile/src/compile/aux_gen/cases_on.rs @@ -10,8 +10,8 @@ //! //! Follows `refs/lean4/src/library/constructions/cases_on.cpp`. -use crate::ix::compile::aux_gen::AuxDef; -use crate::ix::env::{ +use crate::compile::aux_gen::AuxDef; +use ix_common::env::{ BinderInfo, ConstantInfo, Env as LeanEnv, Expr as LeanExpr, ExprData, Level, Name, RecursorVal, }; @@ -53,7 +53,7 @@ fn mk_pi_unit(e: &LeanExpr, unit: &LeanExpr) -> LeanExpr { /// Uses FVar-based construction: opens the rec type into FVars, builds /// casesOn type and value using FVar references, then abstracts with /// mk_forall/mk_lambda. -pub(crate) fn generate_cases_on( +pub fn generate_cases_on( name: &Name, rec_val: &RecursorVal, lean_env: &LeanEnv, @@ -65,7 +65,7 @@ pub(crate) fn generate_cases_on( // Extract target inductive name from "A.casesOn" → "A" let target_ind = match name.as_data() { - crate::ix::env::NameData::Str(parent, s, _) if s == "casesOn" => { + ix_common::env::NameData::Str(parent, s, _) if s == "casesOn" => { parent.clone() }, _ => return None, @@ -370,8 +370,8 @@ fn get_minor_name( #[cfg(test)] mod tests { use super::*; - use crate::ix::env::{BinderInfo, ConstantVal, InductiveVal, Literal}; - use lean_ffi::nat::Nat; + use bignat::Nat; + use ix_common::env::{BinderInfo, ConstantVal, InductiveVal, Literal}; fn mk_name_for(s: &str) -> Name { let mut n = Name::anon(); diff --git a/src/ix/compile/aux_gen/expr_utils.rs b/crates/compile/src/compile/aux_gen/expr_utils.rs similarity index 96% rename from src/ix/compile/aux_gen/expr_utils.rs rename to crates/compile/src/compile/aux_gen/expr_utils.rs index 986dc6f02..92105974d 100644 --- a/src/ix/compile/aux_gen/expr_utils.rs +++ b/crates/compile/src/compile/aux_gen/expr_utils.rs @@ -9,14 +9,14 @@ use rustc_hash::{FxHashMap, FxHashSet}; -use crate::ix::address::Address; -use crate::ix::compile::nat_conv::{nat_to_u64, nat_to_usize}; -use crate::ix::env::{ +use crate::compile::nat_conv::{nat_to_u64, nat_to_usize}; +use bignat::Nat; +use ix_common::address::Address; +use ix_common::env::{ BinderInfo, Expr as LeanExpr, ExprData, Level, LevelData, Name, }; -use crate::ix::kernel::ingress::{lean_level_to_kuniv, resolve_lean_name_addr}; -use crate::ix::kernel::mode::Meta; -use lean_ffi::nat::Nat; +use ix_kernel::ingress::{lean_level_to_kuniv, resolve_lean_name_addr}; +use ix_kernel::mode::Meta; // ========================================================================= // FVar infrastructure @@ -28,7 +28,7 @@ use lean_ffi::nat::Nat; /// FVar space. The `fvar_name` is a unique identifier; `binder_name` is /// the cosmetic name that appears in the final forall/lambda chain. #[derive(Clone)] -pub(crate) struct LocalDecl { +pub struct LocalDecl { pub fvar_name: Name, pub binder_name: Name, pub domain: LeanExpr, @@ -36,7 +36,7 @@ pub(crate) struct LocalDecl { } /// Create a fresh FVar with a unique name derived from `prefix` and `idx`. -pub(crate) fn fresh_fvar(prefix: &str, idx: usize) -> (Name, LeanExpr) { +pub fn fresh_fvar(prefix: &str, idx: usize) -> (Name, LeanExpr) { let name = Name::str(Name::anon(), format!("_{}_{}", prefix, idx)); let fvar = LeanExpr::fvar(name.clone()); (name, fvar) @@ -119,13 +119,13 @@ pub(super) struct IndRecInfo { /// loop body and terminates peeling, potentially yielding a shorter /// `indices` vec than Lean's stored `num_indices`. pub(super) fn decompose_inductive_type( - ind: &crate::ix::env::InductiveVal, + ind: &ix_common::env::InductiveVal, ind_univs: &[Level], param_fvars: &[LocalDecl], - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, -) -> Result { - use crate::ix::ixon::CompileError; + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, +) -> Result { + use ixon::CompileError; let n_params = param_fvars.len(); let ty = subst_levels(&ind.cnst.typ, &ind.cnst.level_params, ind_univs); @@ -274,7 +274,7 @@ pub(super) fn decompose_inductive_type( /// position MUST verify `decls.len() == n` before indexing — otherwise /// a surprising input shape becomes a panic. Prefer /// [`forall_telescope_exact`] when a precise arity is required. -pub(crate) fn forall_telescope( +pub fn forall_telescope( expr: &LeanExpr, n: usize, prefix: &str, @@ -322,10 +322,7 @@ pub(super) fn forall_telescope_exact( start_idx: usize, context: &str, what: &str, -) -> Result< - (Vec, Vec, LeanExpr), - crate::ix::ixon::CompileError, -> { +) -> Result<(Vec, Vec, LeanExpr), ixon::CompileError> { let (fvars, decls, body) = forall_telescope(expr, n, prefix, start_idx); if decls.len() != n { // Include enough context to pinpoint the shape problem: every peeled @@ -337,7 +334,7 @@ pub(super) fn forall_telescope_exact( format!("{}:{}", d.binder_name.pretty(), describe_expr_head(&d.domain)) }) .collect(); - return Err(crate::ix::ixon::CompileError::UnsupportedExpr { + return Err(ixon::CompileError::UnsupportedExpr { desc: format!( "{context}: expected {n} leading foralls ({what}), got {actual}. \ Peeled binders (name:domain_kind): [{binders}]. \ @@ -447,7 +444,7 @@ pub(super) fn mk_forall(body: LeanExpr, binders: &[LocalDecl]) -> LeanExpr { /// Build a lambda chain by batch-abstracting all FVars in a single pass. /// /// Same semantics as `mk_forall` but produces `λ (x : T), body`. -pub(crate) fn mk_lambda(body: LeanExpr, binders: &[LocalDecl]) -> LeanExpr { +pub fn mk_lambda(body: LeanExpr, binders: &[LocalDecl]) -> LeanExpr { mk_binder_chain(body, binders, BinderKind::Lambda) } @@ -601,10 +598,7 @@ pub(super) fn batch_abstract( /// /// `instantiate1` is used when peeling forall binders during recursor /// construction (matching Lean C++ and lean4lean). -pub(crate) fn instantiate1( - body: &LeanExpr, - replacement: &LeanExpr, -) -> LeanExpr { +pub fn instantiate1(body: &LeanExpr, replacement: &LeanExpr) -> LeanExpr { instantiate1_at(body, replacement, 0) } @@ -862,7 +856,7 @@ pub(super) fn shift_vars( // ========================================================================= /// Substitute universe parameters in expressions. -pub(crate) fn subst_levels( +pub fn subst_levels( expr: &LeanExpr, params: &[Name], univs: &[Level], @@ -1259,7 +1253,7 @@ impl<'a> RestoreState<'a> { } /// Open lambda binders into FVars (matching forall_telescope but for lambdas). -pub(crate) fn lambda_telescope( +pub fn lambda_telescope( expr: &LeanExpr, n: usize, prefix: &str, @@ -1490,7 +1484,7 @@ pub(super) fn mk_const(name: &Name, univs: &[Level]) -> LeanExpr { /// /// Called by the kernel's `mk_local_decl` during inductive processing /// to ensure parameter/field types are clean before entering the local context. -pub(crate) fn consume_type_annotations(e: &LeanExpr) -> LeanExpr { +pub fn consume_type_annotations(e: &LeanExpr) -> LeanExpr { let (head, args) = decompose_apps(e); if let ExprData::Const(name, _, _) = head.as_data() { let n = name.pretty(); @@ -1507,7 +1501,7 @@ pub(crate) fn consume_type_annotations(e: &LeanExpr) -> LeanExpr { } /// Decompose an application spine: `f a1 a2 ... an` -> `(f, [a1, ..., an])`. -pub(crate) fn decompose_apps(expr: &LeanExpr) -> (LeanExpr, Vec) { +pub fn decompose_apps(expr: &LeanExpr) -> (LeanExpr, Vec) { let mut args = Vec::new(); let mut cur = expr.clone(); while let ExprData::App(f, a, _) = cur.as_data() { @@ -1724,14 +1718,14 @@ pub(super) fn find_motive_fvar( /// ``` /// Ensure PUnit and PProd are in the given kenv for kernel type inference. /// Accepts `kctx` so callers can choose which KernelCtx to populate. -pub(crate) fn ensure_prelude_in_kenv_of( - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, +pub fn ensure_prelude_in_kenv_of( + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, ) { - use crate::ix::kernel::constant::KConst; - use crate::ix::kernel::expr::KExpr; - use crate::ix::kernel::id::KId; - use crate::ix::kernel::level::KUniv; + use ix_kernel::constant::KConst; + use ix_kernel::expr::KExpr; + use ix_kernel::id::KId; + use ix_kernel::level::KUniv; let n2a = Some(&stt.name_to_addr); let aux_n2a = Some(&stt.aux_name_to_addr); @@ -1950,17 +1944,15 @@ pub(crate) fn ensure_prelude_in_kenv_of( /// requires the parent). fn ensure_in_kenv_of_inner_env( name: &Name, - lean_env: &crate::ix::env::Env, - stt: &crate::ix::compile::CompileState, - kenv: &mut crate::ix::kernel::env::KEnv, + lean_env: &ix_common::env::Env, + stt: &crate::compile::CompileState, + kenv: &mut ix_kernel::env::KEnv, replace_axio_stub: bool, ) { - use crate::ix::env::{ConstantInfo as LCI, DefinitionSafety}; - use crate::ix::kernel::constant::KConst; - use crate::ix::kernel::id::KId; - use crate::ix::kernel::ingress::{ - lean_expr_to_zexpr_cached, param_names_hash, - }; + use ix_common::env::{ConstantInfo as LCI, DefinitionSafety}; + use ix_kernel::constant::KConst; + use ix_kernel::id::KId; + use ix_kernel::ingress::{lean_expr_to_zexpr_cached, param_names_hash}; let n2a = Some(&stt.name_to_addr); let aux_n2a = Some(&stt.aux_name_to_addr); @@ -1982,10 +1974,10 @@ fn ensure_in_kenv_of_inner_env( // Helper: convert a LeanExpr to KExpr with the given level param names, // using the KEnv's persistent ingress cache. Callers are top-level, so // we start with an empty binder-name stack. - let to_z = |expr: &crate::ix::env::Expr, + let to_z = |expr: &ix_common::env::Expr, lp: &[Name], - kenv: &mut crate::ix::kernel::env::KEnv| - -> crate::ix::kernel::expr::KExpr { + kenv: &mut ix_kernel::env::KEnv| + -> ix_kernel::expr::KExpr { let pn_h = param_names_hash(lp); let mut binder_names: Vec = Vec::new(); lean_expr_to_zexpr_cached( @@ -2059,7 +2051,7 @@ fn ensure_in_kenv_of_inner_env( KConst::Defn { name: name.clone(), level_params: lp.clone(), - kind: crate::ix::ixon::constant::DefKind::Definition, + kind: ixon::constant::DefKind::Definition, safety: d.safety, hints: d.hints, lvls: lp.len() as u64, @@ -2079,9 +2071,9 @@ fn ensure_in_kenv_of_inner_env( KConst::Defn { name: name.clone(), level_params: lp.clone(), - kind: crate::ix::ixon::constant::DefKind::Theorem, + kind: ixon::constant::DefKind::Theorem, safety: DefinitionSafety::Safe, - hints: crate::ix::env::ReducibilityHints::Opaque, + hints: ix_common::env::ReducibilityHints::Opaque, lvls: lp.len() as u64, ty, val, @@ -2099,9 +2091,9 @@ fn ensure_in_kenv_of_inner_env( KConst::Defn { name: name.clone(), level_params: lp.clone(), - kind: crate::ix::ixon::constant::DefKind::Opaque, + kind: ixon::constant::DefKind::Opaque, safety: DefinitionSafety::Safe, - hints: crate::ix::env::ReducibilityHints::Opaque, + hints: ix_common::env::ReducibilityHints::Opaque, lvls: lp.len() as u64, ty, val, @@ -2157,9 +2149,9 @@ fn ensure_in_kenv_of_inner_env( fn ensure_in_kenv_of_inner( name: &Name, - lean_env: &crate::ix::env::Env, - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + lean_env: &ix_common::env::Env, + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, replace_axio_stub: bool, ) { ensure_in_kenv_of_inner_env( @@ -2171,11 +2163,11 @@ fn ensure_in_kenv_of_inner( ); } -pub(crate) fn ensure_in_kenv_of( +pub fn ensure_in_kenv_of( name: &Name, - lean_env: &crate::ix::env::Env, - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + lean_env: &ix_common::env::Env, + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, ) { ensure_in_kenv_of_inner(name, lean_env, stt, kctx, false); } @@ -2183,30 +2175,30 @@ pub(crate) fn ensure_in_kenv_of( /// Like [`ensure_in_kenv_of`], but upgrades an existing type-only `Axio` /// stub into the real constant. This is required before WHNF paths that must /// unfold reducible definitions or inspect inductive/ctor metadata. -pub(crate) fn ensure_full_in_kenv_of( +pub fn ensure_full_in_kenv_of( name: &Name, - lean_env: &crate::ix::env::Env, - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + lean_env: &ix_common::env::Env, + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, ) { ensure_in_kenv_of_inner(name, lean_env, stt, kctx, true); } fn ensure_full_in_tc_env( name: &Name, - lean_env: &crate::ix::env::Env, - stt: &crate::ix::compile::CompileState, - kenv: &mut crate::ix::kernel::env::KEnv, + lean_env: &ix_common::env::Env, + stt: &crate::compile::CompileState, + kenv: &mut ix_kernel::env::KEnv, ) { ensure_in_kenv_of_inner_env(name, lean_env, stt, kenv, true); } /// Convenience wrapper: ingress into the **original** kenv (`stt.kctx`). -pub(crate) fn ensure_in_kenv( +pub fn ensure_in_kenv( name: &Name, - lean_env: &crate::ix::env::Env, - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + lean_env: &ix_common::env::Env, + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, ) { ensure_in_kenv_of(name, lean_env, stt, kctx); } @@ -2224,8 +2216,8 @@ pub(super) struct TcScope<'a> { fvar_levels: FxHashMap, base_depth: usize, param_names: &'a [Name], - stt: &'a crate::ix::compile::CompileState, - tc: crate::ix::kernel::tc::TypeChecker<'a, Meta>, + stt: &'a crate::compile::CompileState, + tc: ix_kernel::tc::TypeChecker<'a, Meta>, /// How many extra locals are currently pushed above base_depth. extra_locals: usize, } @@ -2235,8 +2227,8 @@ impl<'a> TcScope<'a> { pub(super) fn new( outer_fvar_ctx: &[LocalDecl], param_names: &'a [Name], - stt: &'a crate::ix::compile::CompileState, - kctx: &'a mut crate::ix::compile::KernelCtx, + stt: &'a crate::compile::CompileState, + kctx: &'a mut crate::compile::KernelCtx, ) -> Self { let fvar_levels: FxHashMap = outer_fvar_ctx .iter() @@ -2244,7 +2236,7 @@ impl<'a> TcScope<'a> { .map(|(i, decl)| (decl.fvar_name.clone(), i)) .collect(); - let mut tc = crate::ix::kernel::tc::TypeChecker::new(&mut kctx.kenv); + let mut tc = ix_kernel::tc::TypeChecker::new(&mut kctx.kenv); tc.infer_only = true; // Push outer FVar types once. @@ -2347,9 +2339,9 @@ impl<'a> TcScope<'a> { fn get_level_error( &self, ty: &LeanExpr, - kexpr: &crate::ix::kernel::expr::KExpr, - e: &crate::ix::kernel::error::TcError, - ) -> crate::ix::ixon::CompileError { + kexpr: &ix_kernel::expr::KExpr, + e: &ix_kernel::error::TcError, + ) -> ixon::CompileError { eprintln!("[TcScope::get_level] FAILED"); eprintln!(" lean_expr: {}", ty.pretty()); eprintln!(" kexpr: {kexpr}"); @@ -2361,10 +2353,10 @@ impl<'a> TcScope<'a> { self.extra_locals ); // Dump kenv entries for constants referenced in the expression. - let mut stack: Vec<&crate::ix::kernel::expr::KExpr> = vec![kexpr]; + let mut stack: Vec<&ix_kernel::expr::KExpr> = vec![kexpr]; let mut seen_ids = std::collections::HashSet::new(); while let Some(expr) = stack.pop() { - use crate::ix::kernel::expr::ExprData as ZED; + use ix_kernel::expr::ExprData as ZED; match expr.data() { ZED::Const(id, us, _) => { if seen_ids.insert(id.clone()) { @@ -2391,7 +2383,7 @@ impl<'a> TcScope<'a> { _ => {}, } } - crate::ix::ixon::CompileError::UnsupportedExpr { + ixon::CompileError::UnsupportedExpr { desc: format!( "TcScope::get_level({}): tc.infer failed: {e}", ty.pretty() @@ -2412,7 +2404,7 @@ impl<'a> TcScope<'a> { pub(super) fn get_level( &mut self, ty: &LeanExpr, - ) -> Result { + ) -> Result { // Fast path: read Sort level from stored type (matching Lean's // inferAppType which peels foralls without substituting term args). // Sort levels use level params, not BVars, so the level is correct @@ -2432,14 +2424,14 @@ impl<'a> TcScope<'a> { let inferred = loop { match self.tc.infer(&kexpr) { Ok(inferred) => break inferred, - Err(crate::ix::kernel::error::TcError::UnknownConst(addr)) + Err(ix_kernel::error::TcError::UnknownConst(addr)) if faulted_addrs.insert(addr.clone()) && self.fault_in_addr(&addr) => {}, Err(e) => return Err(self.get_level_error(ty, &kexpr, &e)), } }; let ku = self.tc.ensure_sort(&inferred).map_err(|e| { - crate::ix::ixon::CompileError::UnsupportedExpr { + ixon::CompileError::UnsupportedExpr { desc: format!("TcScope::get_level: ensure_sort failed: {e}"), } })?; @@ -2463,7 +2455,7 @@ impl<'a> TcScope<'a> { /// Check if a Level is guaranteed non-zero. Matches Lean's `is_not_zero`: /// true for Succ(_), Param, Max(a,b) where either is not-zero. fn is_not_zero_level(l: &Level) -> bool { - use crate::ix::env::LevelData; + use ix_common::env::LevelData; match l.as_data() { LevelData::Succ(_, _) => true, LevelData::Max(a, b, _) => { @@ -2484,8 +2476,8 @@ impl<'a> TcScope<'a> { /// application, not enough foralls, result isn't Sort, or the constant /// isn't found in the kernel env). fn try_infer_app_sort_level(&self, ty: &LeanExpr) -> Option { - use crate::ix::env::ExprData; - use crate::ix::kernel::expr::ExprData as ZED; + use ix_common::env::ExprData; + use ix_kernel::expr::ExprData as ZED; // Decompose into head constant + args. let (head, args) = decompose_apps(ty); @@ -2498,7 +2490,7 @@ impl<'a> TcScope<'a> { let n2a = Some(&self.stt.name_to_addr); let aux_n2a = Some(&self.stt.aux_name_to_addr); let addr = resolve_lean_name_addr(name, n2a, aux_n2a); - let kid = crate::ix::kernel::id::KId::new(addr, name.clone()); + let kid = ix_kernel::id::KId::new(addr, name.clone()); let kconst = self.tc.env.get(&kid)?; let kty = kconst.ty(); @@ -2535,10 +2527,10 @@ impl<'a> TcScope<'a> { /// the concrete levels from a Const's level args. fn kuniv_to_level_with_const_levels( &self, - u: &crate::ix::kernel::level::KUniv, + u: &ix_kernel::level::KUniv, const_levels: &[Level], ) -> Level { - use crate::ix::kernel::level::UnivData; + use ix_kernel::level::UnivData; match u.data() { UnivData::Zero(_) => Level::zero(), UnivData::Succ(inner, _) => { @@ -2681,13 +2673,13 @@ impl<'a> TcScope<'a> { /// `Mdata` layers carried by the kernel expression are re-wrapped around /// the result in original order — matching `egress_expr`. pub(super) fn kexpr_to_lean( - expr: &crate::ix::kernel::expr::KExpr, + expr: &ix_kernel::expr::KExpr, outer_depth: usize, fvar_levels: &FxHashMap, local_depth: usize, param_names: &[Name], ) -> LeanExpr { - use crate::ix::kernel::expr::ExprData as KED; + use ix_kernel::expr::ExprData as KED; // Reverse `fvar_levels` lazily via linear search — the FVar context is // small in practice (a handful of param/motive/minor/index binders), @@ -2772,11 +2764,11 @@ pub(super) fn kexpr_to_lean( kexpr_to_lean(val, outer_depth, fvar_levels, local_depth, param_names), ), KED::Nat(n, _, _) => { - use crate::ix::env::Literal; + use ix_common::env::Literal; LeanExpr::lit(Literal::NatVal(n.clone())) }, KED::Str(s, _, _) => { - use crate::ix::env::Literal; + use ix_common::env::Literal; LeanExpr::lit(Literal::StrVal(s.clone())) }, }; @@ -2805,8 +2797,8 @@ fn collect_lean_source_name_hints( fvar_levels: &FxHashMap, depth: usize, param_names: &[Name], - stt: &crate::ix::compile::CompileState, - out: &mut FxHashMap, + stt: &crate::compile::CompileState, + out: &mut FxHashMap, ) { if source_name_hint_candidate(source) && !expr_has_bvar(source) { let key = @@ -2909,8 +2901,8 @@ fn restore_lean_source_name_hints( fvar_levels: &FxHashMap, depth: usize, param_names: &[Name], - stt: &crate::ix::compile::CompileState, - hints: &FxHashMap, + stt: &crate::compile::CompileState, + hints: &FxHashMap, ) -> LeanExpr { if source_name_hint_candidate(generated) && !expr_has_bvar(generated) { let key = to_kexpr_static(generated, fvar_levels, depth, param_names, stt) @@ -3061,7 +3053,7 @@ fn expr_has_bvar(expr: &LeanExpr) -> bool { fn restore_source_names_same_content( generated: &LeanExpr, source: &LeanExpr, - stt: &crate::ix::compile::CompileState, + stt: &crate::compile::CompileState, ) -> LeanExpr { let source = strip_mdata_ref(source); @@ -3077,7 +3069,7 @@ fn restore_source_names_same_content( fn restore_source_names_same_content_inner( generated: &LeanExpr, source: &LeanExpr, - stt: &crate::ix::compile::CompileState, + stt: &crate::compile::CompileState, ) -> LeanExpr { match (generated.as_data(), source.as_data()) { ( @@ -3146,7 +3138,7 @@ fn strip_mdata_ref(mut expr: &LeanExpr) -> &LeanExpr { fn same_resolved_name_addr( a: &Name, b: &Name, - stt: &crate::ix::compile::CompileState, + stt: &crate::compile::CompileState, ) -> bool { if a == b { return true; @@ -3167,13 +3159,13 @@ fn to_kexpr_static( fvar_levels: &FxHashMap, ctx_depth: usize, param_names: &[Name], - stt: &crate::ix::compile::CompileState, -) -> crate::ix::kernel::expr::KExpr { + stt: &crate::compile::CompileState, +) -> ix_kernel::expr::KExpr { let n2a = Some(&stt.name_to_addr); let aux_n2a = Some(&stt.aux_name_to_addr); - use crate::ix::kernel::expr::KExpr; - use crate::ix::kernel::id::KId; - use crate::ix::kernel::level::KUniv; + use ix_kernel::expr::KExpr; + use ix_kernel::id::KId; + use ix_kernel::level::KUniv; match expr.as_data() { ExprData::Fvar(fname, _) => { @@ -3225,7 +3217,7 @@ fn to_kexpr_static( KExpr::prj(zid, nat_to_u64(idx), ke) }, ExprData::Lit(lit, _) => { - use crate::ix::env::Literal; + use ix_common::env::Literal; match lit { Literal::NatVal(n) => { let addr = Address::hash(&nat_to_u64(n).to_le_bytes()); @@ -3277,7 +3269,7 @@ fn collect_lean_const_refs(expr: &LeanExpr, out: &mut FxHashSet) { #[cfg(test)] mod tests { use super::*; - use crate::ix::env::BinderInfo; + use ix_common::env::BinderInfo; fn mk_name_for(s: &str) -> Name { let mut n = Name::anon(); @@ -3654,7 +3646,7 @@ mod tests { fn consume_type_annotations_strips_known_wrappers() { // `outParam α` reduces to `α`. We use a stub inductive name that the // function recognizes. - use crate::ix::env::BinderInfo; + use ix_common::env::BinderInfo; let inner = sort0(); let wrapped = LeanExpr::app( LeanExpr::cnst(mk_name_for("outParam"), vec![]), diff --git a/src/ix/compile/aux_gen/nested.rs b/crates/compile/src/compile/aux_gen/nested.rs similarity index 98% rename from src/ix/compile/aux_gen/nested.rs rename to crates/compile/src/compile/aux_gen/nested.rs index 726f70a17..5a57d2c28 100644 --- a/src/ix/compile/aux_gen/nested.rs +++ b/crates/compile/src/compile/aux_gen/nested.rs @@ -14,26 +14,26 @@ //! - Spec_params are built in FVar space during detection, then abstracted back //! to BVars for the returned `CompileFlatMember`. +use bignat::Nat; use blake3::Hash; -use lean_ffi::nat::Nat; use rustc_hash::{FxHashMap, FxHashSet}; use super::expr_utils::{ LocalDecl, batch_abstract, decompose_apps, forall_telescope, instantiate_pi_params, instantiate1, mk_forall, subst_levels, }; -use crate::ix::compile::nat_conv::{nat_to_u64, nat_to_usize}; -use crate::ix::env::{ +use crate::compile::nat_conv::{nat_to_u64, nat_to_usize}; +use ix_common::env::{ ConstantInfo, Env as LeanEnv, Expr as LeanExpr, ExprData, Level, Name, }; -use crate::ix::ixon::CompileError; +use ixon::CompileError; /// A member of the flat block (original inductive or nested auxiliary). /// /// Spec_params use BVars relative to the block's parameter context: /// `BVar(0)` = innermost (last) param, `BVar(n_params-1)` = outermost (first). #[derive(Clone)] -pub(crate) struct CompileFlatMember { +pub struct CompileFlatMember { pub name: Name, pub spec_params: Vec, pub occurrence_level_args: Vec, @@ -51,7 +51,7 @@ pub(crate) struct CompileFlatMember { /// Matches the C++ kernel's `elim_nested_inductive_result`: auxiliary types /// like `_nested.Array_1` replace `Array (Part α β)` so that the recursor /// generator can treat all members uniformly. -pub(crate) struct ExpandedBlock { +pub struct ExpandedBlock { /// All types in the expanded block: originals first, then auxiliaries. pub types: Vec, /// `aux_name → nested_expr`: the original nested application with block @@ -82,7 +82,7 @@ pub(crate) struct ExpandedBlock { /// All members share the same `level_params` and `n_params` — auxiliaries /// have the block's parameters, not the external inductive's own parameters. #[derive(Clone)] -pub(crate) struct ExpandedMember { +pub struct ExpandedMember { /// Inductive name: original name for originals, `_nested.ExtInd_N` for /// auxiliaries (scoped under `all[0]`). pub name: Name, @@ -101,7 +101,7 @@ pub(crate) struct ExpandedMember { /// A constructor in the expanded block. #[derive(Clone)] -pub(crate) struct ExpandedCtor { +pub struct ExpandedCtor { /// Constructor name: for auxiliaries, prefixed with aux name. pub name: Name, /// Constructor type with nested refs replaced by aux const applications. @@ -424,7 +424,7 @@ impl<'a> ExpandCtx<'a> { /// /// Matches the C++ kernel's `elim_nested_inductive_fn::operator()()` at /// `refs/lean4/src/kernel/inductive.cpp:1045-1077`. -pub(crate) fn expand_nested_block( +pub fn expand_nested_block( ordered_originals: &[Name], lean_env: &LeanEnv, alias_to_rep: &FxHashMap, @@ -612,9 +612,9 @@ pub(crate) fn expand_nested_block( /// appear in user-visible env: `RestoreCtx` converts them back to /// `ExtInd spec_params` expressions during recursor emission. So renaming /// them by canonical index is purely an internal-labeling change. -pub(crate) fn sort_aux_by_partition_refinement( +pub fn sort_aux_by_partition_refinement( expanded: &mut ExpandedBlock, - stt: &crate::ix::compile::CompileState, + stt: &crate::compile::CompileState, ) -> Result, CompileError> { let n_originals = expanded.n_originals; let n_total = expanded.types.len(); @@ -629,9 +629,9 @@ pub(crate) fn sort_aux_by_partition_refinement( // fixed positional MutRef, so alpha-equivalent originals collapse to the same // aux signature. If any referenced original is unresolved, compare_expr now // errors instead of falling back to namespace-sensitive name hashes. - use crate::ix::compile::{BlockCache, sort_consts}; - use crate::ix::env::{ConstantVal, ConstructorVal, InductiveVal}; - use crate::ix::mutual::{Ind, MutConst}; + use crate::compile::{BlockCache, sort_consts}; + use crate::mutual::{Ind, MutConst}; + use ix_common::env::{ConstantVal, ConstructorVal, InductiveVal}; let level_params = expanded.level_params.clone(); @@ -780,7 +780,7 @@ pub(crate) fn sort_aux_by_partition_refinement( let nested_prefix = { let first_aux_name = &expanded.types[n_originals].name; match first_aux_name.as_data() { - crate::ix::env::NameData::Str(prefix, _, _) => prefix.clone(), + ix_common::env::NameData::Str(prefix, _, _) => prefix.clone(), _ => { return Err(CompileError::InvalidMutualBlock { reason: format!( @@ -806,7 +806,7 @@ pub(crate) fn sort_aux_by_partition_refinement( // Extract the "" identifier from old suffix. let ext_name = match old_name.as_data() { - crate::ix::env::NameData::Str(_, suffix, _) => { + ix_common::env::NameData::Str(_, suffix, _) => { // Old suffix is "_" — strip the trailing "_". let s: &str = suffix.as_ref(); // Find the last underscore — everything before is "". @@ -940,7 +940,7 @@ pub(crate) fn sort_aux_by_partition_refinement( /// /// `original_all` is the source-order Lean `InductiveVal.all` list — /// not alpha-collapsed representatives, and not canonical-aux-sorted. -pub(crate) fn source_aux_order( +pub fn source_aux_order( original_all: &[Name], lean_env: &LeanEnv, ) -> Result)>, CompileError> { @@ -954,7 +954,7 @@ pub(crate) fn source_aux_order( /// Like [`source_aux_order`], but also reports the source mutual-block member /// whose constructor walk first discovered each auxiliary. -pub(crate) fn source_aux_order_with_owner( +pub fn source_aux_order_with_owner( original_all: &[Name], lean_env: &LeanEnv, ) -> Result)>, CompileError> { @@ -990,7 +990,7 @@ fn source_aux_order_from_expanded( /// source auxes whose spec_params reference inductives that belong to /// a different SCC block — those auxes are handled by that block's /// compilation, not ours. -pub(crate) const PERM_OUT_OF_SCC: usize = usize::MAX; +pub const PERM_OUT_OF_SCC: usize = usize::MAX; /// Compute the permutation mapping Lean-source aux-walk positions to /// canonical aux positions. Returns `perm: Vec` @@ -1024,11 +1024,11 @@ pub(crate) const PERM_OUT_OF_SCC: usize = usize::MAX; /// Returns an error if some canonical aux has no matching source. This /// shouldn't happen because canonical members are always a subset (via /// dedup) of what a full source walk would find. -pub(crate) fn compute_aux_perm( +pub fn compute_aux_perm( expanded: &ExpandedBlock, original_all: &[Name], lean_env: &LeanEnv, - stt: &crate::ix::compile::CompileState, + stt: &crate::compile::CompileState, orig_to_canon_names: &std::collections::HashMap, ) -> Result, CompileError> { let n_originals = expanded.n_originals; @@ -1208,12 +1208,12 @@ pub(crate) fn compute_aux_perm( fn aux_spec_eq( canon: &LeanExpr, src: &LeanExpr, - stt: &crate::ix::compile::CompileState, + stt: &crate::compile::CompileState, source_to_canon_fvar: &FxHashMap, cache: &mut FxHashMap<(Hash, Hash), bool>, ) -> bool { - let canon = crate::ix::congruence::strip_mdata(canon); - let src = crate::ix::congruence::strip_mdata(src); + let canon = crate::congruence::strip_mdata(canon); + let src = crate::congruence::strip_mdata(src); let key = (*canon.get_hash(), *src.get_hash()); if let Some(cached) = cache.get(&key) { @@ -1226,7 +1226,7 @@ fn aux_spec_eq( source_to_canon_fvar.get(b).map_or(a == b, |expected| a == expected) }, (ExprData::Sort(a, _), ExprData::Sort(b, _)) => { - crate::ix::congruence::level_alpha_eq(a, b).is_ok() + crate::congruence::level_alpha_eq(a, b).is_ok() }, ( ExprData::Const(a_name, a_lvls, _), @@ -1236,7 +1236,7 @@ fn aux_spec_eq( || a_lvls .iter() .zip(b_lvls.iter()) - .any(|(a, b)| crate::ix::congruence::level_alpha_eq(a, b).is_err()) + .any(|(a, b)| crate::congruence::level_alpha_eq(a, b).is_err()) { return false; } @@ -1669,7 +1669,7 @@ struct FvarFlatMember { /// caught by checking for non-param FVars in the detected spec_params. /// /// Ported from the kernel's `build_flat_block` (`src/ix/kernel/inductive.rs:364-475`). -pub(crate) fn build_compile_flat_block( +pub fn build_compile_flat_block( ordered_originals: &[Name], lean_env: &LeanEnv, ) -> Result, CompileError> { @@ -1680,7 +1680,7 @@ pub(crate) fn build_compile_flat_block( /// environment first for all lookups. Used by the expand/restore path /// to scan expanded constructor types (where nested refs are already /// replaced with auxiliary const applications). -pub(crate) fn build_compile_flat_block_with_overlay( +pub fn build_compile_flat_block_with_overlay( ordered_originals: &[Name], lean_env: &LeanEnv, overlay: Option<&LeanEnv>, @@ -1905,7 +1905,7 @@ fn abstract_spec_params_to_bvars( /// This function computes the pointwise max of `occurrence_level_args` across /// all auxiliaries with the same `name`, then updates all of them. fn maximize_occurrence_levels(flat: &mut [FvarFlatMember], n_originals: usize) { - use crate::ix::env::LevelData; + use ix_common::env::LevelData; use rustc_hash::FxHashMap; // Group auxiliary members by external inductive name. @@ -2080,10 +2080,10 @@ fn try_detect_nested_fvar( #[cfg(test)] mod tests { use super::*; - use crate::ix::env::{ + use bignat::Nat; + use ix_common::env::{ AxiomVal, ConstantVal, InductiveVal, Level as LL, Name, }; - use lean_ffi::nat::Nat; fn mk_name_for(s: &str) -> Name { let mut n = Name::anon(); @@ -2134,7 +2134,7 @@ mod tests { mk_name_for("x"), sort0(), LeanExpr::cnst(mk_name_for("Target"), vec![]), - crate::ix::env::BinderInfo::Default, + ix_common::env::BinderInfo::Default, ); assert!(expr_mentions_any_name(&e, &names_of([mk_name_for("Target")]))); } @@ -2219,7 +2219,7 @@ mod tests { mk_name_for("x"), sort0(), LeanExpr::bvar(Nat::from(0u64)), - crate::ix::env::BinderInfo::Default, + ix_common::env::BinderInfo::Default, ); assert!(!has_invalid_spec_ref(&e, &[])); } @@ -2231,7 +2231,7 @@ mod tests { mk_name_for("x"), sort0(), LeanExpr::fvar(unknown), - crate::ix::env::BinderInfo::Default, + ix_common::env::BinderInfo::Default, ); assert!(has_invalid_spec_ref(&e, &[])); } @@ -2285,7 +2285,7 @@ mod tests { mk_name_for("_"), LeanExpr::cnst(nat_name.clone(), vec![]), LeanExpr::cnst(nat_name.clone(), vec![]), - crate::ix::env::BinderInfo::Default, + ix_common::env::BinderInfo::Default, ), }, is_unsafe: false, diff --git a/src/ix/compile/aux_gen/rec_on.rs b/crates/compile/src/compile/aux_gen/rec_on.rs similarity index 97% rename from src/ix/compile/aux_gen/rec_on.rs rename to crates/compile/src/compile/aux_gen/rec_on.rs index eec09eaa1..2bc934a9a 100644 --- a/src/ix/compile/aux_gen/rec_on.rs +++ b/crates/compile/src/compile/aux_gen/rec_on.rs @@ -7,8 +7,8 @@ //! the FVar/declaration arrays, then close back with mk_forall/mk_lambda. //! Follows `refs/lean4/src/Lean/Meta/Constructions/RecOn.lean`. -use crate::ix::compile::aux_gen::AuxDef; -use crate::ix::env::{Level, Name, RecursorVal}; +use crate::compile::aux_gen::AuxDef; +use ix_common::env::{Level, Name, RecursorVal}; use super::expr_utils::{ forall_telescope, mk_app_n, mk_const, mk_forall, mk_lambda, @@ -17,10 +17,7 @@ use super::expr_utils::{ /// Generate a `.recOn` definition from a canonical `.rec`. /// /// Returns `None` if the recursor type cannot be decomposed. -pub(crate) fn generate_rec_on( - name: &Name, - rec_val: &RecursorVal, -) -> Option { +pub fn generate_rec_on(name: &Name, rec_val: &RecursorVal) -> Option { let n_params = rec_val.num_params.to_u64()? as usize; let n_motives = rec_val.num_motives.to_u64()? as usize; let n_minors = rec_val.num_minors.to_u64()? as usize; @@ -79,8 +76,8 @@ pub(crate) fn generate_rec_on( #[cfg(test)] mod tests { use super::*; - use crate::ix::env::{BinderInfo, ConstantVal, Expr as LeanExpr, ExprData}; - use lean_ffi::nat::Nat; + use bignat::Nat; + use ix_common::env::{BinderInfo, ConstantVal, Expr as LeanExpr, ExprData}; fn mk_name(s: &str) -> Name { Name::str(Name::anon(), s.to_string()) diff --git a/src/ix/compile/aux_gen/recursor.rs b/crates/compile/src/compile/aux_gen/recursor.rs similarity index 96% rename from src/ix/compile/aux_gen/recursor.rs rename to crates/compile/src/compile/aux_gen/recursor.rs index c5c515393..6c8924b6a 100644 --- a/src/ix/compile/aux_gen/recursor.rs +++ b/crates/compile/src/compile/aux_gen/recursor.rs @@ -11,16 +11,14 @@ //! Key difference from C++: we use FVar-based intermediate computation //! (see `expr_utils.rs`) then abstract back into de Bruijn binder chains. -use crate::ix::compile::nat_conv::{ - nat_to_u64, nat_to_usize, try_nat_to_usize, -}; -use crate::ix::env::{ +use crate::compile::nat_conv::{nat_to_u64, nat_to_usize, try_nat_to_usize}; +use bignat::Nat; +use ix_common::env::{ BinderInfo, ConstantInfo, ConstantVal, ConstructorVal, Env as LeanEnv, Expr as LeanExpr, ExprData, InductiveVal, Level, Name, NameData, RecursorRule, RecursorVal, }; -use crate::ix::ixon::CompileError; -use lean_ffi::nat::Nat; +use ixon::CompileError; use super::expr_utils::{ LocalDecl, decompose_apps, fresh_fvar, instantiate_spec_with_fvars, @@ -45,7 +43,7 @@ use super::expr_utils::{ /// /// The caller is responsible for applying `RestoreCtx::restore` to the /// output to replace auxiliary const references with original nested apps. -pub(crate) fn generate_recursors_from_expanded( +pub fn generate_recursors_from_expanded( sorted_classes: &[Vec], expanded: &super::nested::ExpandedBlock, // `source_of_canonical[canonical_i]` = Lean source-walk index `source_j` @@ -57,8 +55,8 @@ pub(crate) fn generate_recursors_from_expanded( // no nested-aux hash-sort permutation. source_of_canonical: Option<&[usize]>, lean_env: &LeanEnv, - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, ) -> Result<(Vec<(Name, RecursorVal)>, bool), CompileError> { if expanded.types.is_empty() { return Ok((vec![], false)); @@ -292,11 +290,11 @@ struct FlatInfo { /// directly and pass the appropriate `KernelCtx` — this wrapper is kept /// only so unit tests don't have to plumb one through. #[cfg(test)] -pub(crate) fn generate_canonical_recursors( +pub fn generate_canonical_recursors( sorted_classes: &[Vec], lean_env: &LeanEnv, - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, ) -> Result<(Vec<(Name, RecursorVal)>, bool), CompileError> { generate_canonical_recursors_with_overlay( sorted_classes, @@ -342,7 +340,7 @@ pub(crate) fn generate_canonical_recursors( fn reorder_flat_by_layout( flat: Vec, n_classes: usize, - layout: &crate::ix::ixon::env::AuxLayout, + layout: &ixon::env::AuxLayout, ) -> Result< Vec, (Vec, String), @@ -434,13 +432,13 @@ fn reorder_flat_by_layout( Ok(primary) } -pub(crate) fn generate_canonical_recursors_with_overlay( +pub fn generate_canonical_recursors_with_overlay( sorted_classes: &[Vec], lean_env: &LeanEnv, overlay: Option<&LeanEnv>, pre_flat: Option>, - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, ) -> Result<(Vec<(Name, RecursorVal)>, bool), CompileError> { generate_canonical_recursors_with_layout( sorted_classes, @@ -455,20 +453,20 @@ pub(crate) fn generate_canonical_recursors_with_overlay( } /// Like [`generate_canonical_recursors_with_overlay`] but accepts an -/// optional [`crate::ix::ixon::env::AuxLayout`] that reorders the aux +/// optional [`ixon::env::AuxLayout`] that reorders the aux /// section of the flat block per its `perm` before recursor generation. /// /// This is the hook decompile uses to pin its canonical layout to /// compile's first-run result. With `aux_layout = None`, falls back to /// the discovery order produced by `build_compile_flat_block_with_overlay`. -pub(crate) fn generate_canonical_recursors_with_layout( +pub fn generate_canonical_recursors_with_layout( sorted_classes: &[Vec], lean_env: &LeanEnv, overlay: Option<&LeanEnv>, pre_flat: Option>, - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, - aux_layout: Option<&crate::ix::ixon::env::AuxLayout>, + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, + aux_layout: Option<&ixon::env::AuxLayout>, // Optional Lean-source index per canonical aux position, used for // emitting `all0.rec_{source_j + 1}` names directly. If provided // alongside `aux_layout`, both must agree (this parameter takes @@ -950,8 +948,8 @@ fn build_rec_type( rec_level_params: &[Name], lean_env: &LeanEnv, overlay: Option<&LeanEnv>, - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, nested_rewrite: Option<&mut NestedRewriteCtx>, ) -> LeanExpr { let env_get = |name: &Name| -> Option { @@ -1350,8 +1348,8 @@ fn build_minor_type( motive_fvars: &[LeanExpr], ind_univs: &[Level], rec_level_params: &[Name], - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, // Shared scratch for nested-aux level rewrites across every ctor in // the block. `None` when the block doesn't need any rewriting. nested_rewrite: Option<&mut NestedRewriteCtx>, @@ -1631,8 +1629,8 @@ fn build_rec_rules( // Lean-source-indexed aux naming (see caller doc). `None` falls back // to `canonical_i + 1`. source_of_canonical: Option<&[usize]>, - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, nested_rewrite: Option<&mut NestedRewriteCtx>, ) -> Result, CompileError> { let _ = n_classes; // Kept for signature parity with `build_rec_type`. @@ -2002,7 +2000,7 @@ fn build_rule_ih_fvar( /// Lean's `appendAfter`: append a suffix string to a Name. /// /// Matches `Init/Meta/Defs.lean:317-320`: -/// ``` +/// ```text /// def appendAfter (n : Name) (suffix : String) : Name := /// n.modifyBase fun /// | str p s => Name.mkStr p (s ++ suffix) @@ -2096,7 +2094,7 @@ fn find_rec_target( param_fvars: &[LeanExpr], n_params: usize, scope: &mut super::expr_utils::TcScope<'_>, - _stt: &crate::ix::compile::CompileState, + _stt: &crate::compile::CompileState, ) -> Option { // Phase 1: syntactic peel + match. let mut ty = dom.clone(); @@ -2330,15 +2328,15 @@ fn compute_is_large_and_k( n_classes: usize, n_params: usize, lean_env: &LeanEnv, - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, ) -> Result<(bool, bool, bool), CompileError> { - use crate::ix::kernel::constant::KConst; - use crate::ix::kernel::id::KId; - use crate::ix::kernel::ingress::{ + use ix_kernel::constant::KConst; + use ix_kernel::id::KId; + use ix_kernel::ingress::{ lean_expr_to_zexpr_with_kenv, resolve_lean_name_addr, }; - use crate::ix::kernel::mode::Meta; + use ix_kernel::mode::Meta; let n2a = Some(&stt.name_to_addr); let aux_n2a = Some(&stt.aux_name_to_addr); @@ -2351,7 +2349,7 @@ fn compute_is_large_and_k( u64, u64, Vec>, - crate::ix::kernel::expr::KExpr, + ix_kernel::expr::KExpr, bool, )> = Vec::new(); @@ -2459,7 +2457,7 @@ fn compute_is_large_and_k( let first_n_indices = ind_infos[0].2; // Use the TC for the appropriate context. - let mut tc = crate::ix::kernel::tc::TypeChecker::new(&mut kctx.kenv); + let mut tc = ix_kernel::tc::TypeChecker::new(&mut kctx.kenv); // Compute the WHNF-reduced result sort level via the kernel. This peels // params+indices with whnf at each step — crucial for inductives whose @@ -2533,7 +2531,7 @@ fn compute_is_large_and_k( && is_prop; let _cilk_elapsed = _cilk_start.elapsed(); - if *crate::ix::compile::IX_TIMING && _cilk_elapsed.as_secs_f32() > 0.1 { + if *crate::compile::IX_TIMING && _cilk_elapsed.as_secs_f32() > 0.1 { eprintln!( "[compute_is_large_and_k] {:?} total={:.3}s ingress={:.3}s n_classes={} kenv_size={}", classes[0].ind.cnst.name.pretty(), @@ -2553,8 +2551,8 @@ fn compute_is_large_and_k( fn ingress_target_type_deps( target_ty: &LeanExpr, lean_env: &LeanEnv, - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, ) { let mut seen = rustc_hash::FxHashSet::default(); let mut queue = Vec::new(); @@ -2578,8 +2576,8 @@ fn ingress_field_deps( class: &FlatInfo, _lvl_params: &[Name], lean_env: &LeanEnv, - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, ) { let mut seen = rustc_hash::FxHashSet::default(); let mut queue: Vec = Vec::new(); @@ -2603,8 +2601,8 @@ fn ingress_aux_gen_dep( name: &Name, ci: &ConstantInfo, lean_env: &LeanEnv, - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, queue: &mut Vec, ) { match ci { @@ -2653,15 +2651,15 @@ fn ingress_type_stub( name: &Name, typ: &LeanExpr, level_params: &[Name], - stt: &crate::ix::compile::CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + stt: &crate::compile::CompileState, + kctx: &mut crate::compile::KernelCtx, ) { - use crate::ix::kernel::constant::KConst; - use crate::ix::kernel::id::KId; - use crate::ix::kernel::ingress::{ + use ix_kernel::constant::KConst; + use ix_kernel::id::KId; + use ix_kernel::ingress::{ lean_expr_to_zexpr_with_kenv, resolve_lean_name_addr, }; - use crate::ix::kernel::mode::Meta; + use ix_kernel::mode::Meta; let n2a = Some(&stt.name_to_addr); let aux_n2a = Some(&stt.aux_name_to_addr); @@ -2733,9 +2731,9 @@ fn collect_const_refs(expr: &LeanExpr, out: &mut Vec) { /// referenced by that same comment for the historical record. #[allow(dead_code)] fn peek_result_sort( - ty: &crate::ix::kernel::expr::KExpr, -) -> Option> { - use crate::ix::kernel::expr::ExprData as ZED; + ty: &ix_kernel::expr::KExpr, +) -> Option> { + use ix_kernel::expr::ExprData as ZED; let mut cur = ty.clone(); loop { match cur.data() { @@ -2749,7 +2747,7 @@ fn peek_result_sort( #[cfg(test)] mod tests { use super::*; - use crate::ix::compile::aux_gen::below::{ + use crate::compile::aux_gen::below::{ BelowConstant, generate_below_constants, }; @@ -2877,7 +2875,7 @@ mod tests { } fn insert_aux_stub_def(env: &mut LeanEnv, ind: &Name, suffix: &str) -> Name { - use crate::ix::env::{DefinitionSafety, DefinitionVal, ReducibilityHints}; + use ix_common::env::{DefinitionSafety, DefinitionVal, ReducibilityHints}; let def_name = Name::str(ind.clone(), suffix.into()); env.insert( @@ -3477,8 +3475,8 @@ mod tests { env.insert(ctor_name, ConstantInfo::CtorInfo(ctor)); let classes = vec![vec![ind_name]]; - let tmp_stt = crate::ix::compile::CompileState::default(); - let mut kctx = crate::ix::compile::KernelCtx::new(); + let tmp_stt = crate::compile::CompileState::default(); + let mut kctx = crate::compile::KernelCtx::new(); let (result, _is_prop) = generate_canonical_recursors(&classes, &env, &tmp_stt, &mut kctx) .unwrap(); @@ -3503,8 +3501,8 @@ mod tests { #[test] fn test_aux_gen_alpha_collapse() { let (env, a, b) = build_alpha_collapse_env(); - let stt = crate::ix::compile::CompileState::default(); - let mut kctx = crate::ix::compile::KernelCtx::new(); + let stt = crate::compile::CompileState::default(); + let mut kctx = crate::compile::KernelCtx::new(); // After sort_consts collapse, A≅B → 1 class. let classes = vec![vec![a.clone(), b.clone()]]; @@ -3563,12 +3561,12 @@ mod tests { #[test] fn test_alpha_collapse_sort_consts_groups_inductives() { - use crate::ix::compile::{BlockCache, mk_indc, sort_consts}; - use crate::ix::env::ConstantInfo as LeanCI; - use crate::ix::mutual::MutConst; + use crate::compile::{BlockCache, mk_indc, sort_consts}; + use crate::mutual::MutConst; + use ix_common::env::ConstantInfo as LeanCI; let (env, a, b) = build_alpha_collapse_env(); - let stt = crate::ix::compile::CompileState::default(); + let stt = crate::compile::CompileState::default(); let mut cache = BlockCache::default(); let mut cs = Vec::new(); @@ -3594,7 +3592,7 @@ mod tests { #[test] fn test_alpha_collapse_compile_env_addresses_inductives_and_ctors() { - use crate::ix::compile::env::compile_env; + use crate::compile::env::compile_env; let (env, a, b) = build_alpha_collapse_env(); let lean_env = std::sync::Arc::new(env); @@ -3617,7 +3615,7 @@ mod tests { #[test] fn test_alpha_collapse_aux_gen_aliases_primary_aux_to_rep() { - use crate::ix::compile::aux_gen::{self, PatchedConstant}; + use crate::compile::aux_gen::{self, PatchedConstant}; let (mut env, a, b) = build_alpha_collapse_env(); let all = vec![a.clone(), b.clone()]; @@ -3633,8 +3631,8 @@ mod tests { let a_brecon = insert_aux_stub_def(&mut env, &a, "brecOn"); let b_brecon = insert_aux_stub_def(&mut env, &b, "brecOn"); - let stt = crate::ix::compile::CompileState::default(); - let mut kctx = crate::ix::compile::KernelCtx::new(); + let stt = crate::compile::CompileState::default(); + let mut kctx = crate::compile::KernelCtx::new(); let out = aux_gen::generate_aux_patches( &[vec![a.clone(), b.clone()]], &all, @@ -3675,8 +3673,8 @@ mod tests { #[test] fn test_aux_gen_alpha_collapse_3() { let (env, a, b, c) = build_alpha_collapse_3_env(); - let stt = crate::ix::compile::CompileState::default(); - let mut kctx = crate::ix::compile::KernelCtx::new(); + let stt = crate::compile::CompileState::default(); + let mut kctx = crate::compile::KernelCtx::new(); // All 3 collapse into 1 class. let classes = vec![vec![a.clone(), b.clone(), c.clone()]]; @@ -3710,8 +3708,8 @@ mod tests { #[test] fn test_aux_gen_over_merge_alpha_collapse() { let (env, a, b, c) = build_over_merge_alpha_collapse_env(); - let stt = crate::ix::compile::CompileState::default(); - let mut kctx = crate::ix::compile::KernelCtx::new(); + let stt = crate::compile::CompileState::default(); + let mut kctx = crate::compile::KernelCtx::new(); // A≅B collapse into 1 class, C is a separate class → 2 classes. let classes = vec![vec![a.clone(), b.clone()], vec![c.clone()]]; @@ -3767,8 +3765,8 @@ mod tests { #[test] fn test_aux_gen_over_merge() { let (env, a, b, c) = build_over_merge_env(); - let stt = crate::ix::compile::CompileState::default(); - let mut kctx = crate::ix::compile::KernelCtx::new(); + let stt = crate::compile::CompileState::default(); + let mut kctx = crate::compile::KernelCtx::new(); // No alpha-collapse: A≠B (B has 2 fields), A≠C, B≠C → 3 classes. let classes = vec![vec![a.clone()], vec![b.clone()], vec![c.clone()]]; @@ -3810,8 +3808,8 @@ mod tests { #[test] fn test_aux_gen_below_indc_prop() { let (env, a, b) = build_alpha_collapse_env(); - let stt = crate::ix::compile::CompileState::default(); - let mut kctx = crate::ix::compile::KernelCtx::new(); + let stt = crate::compile::CompileState::default(); + let mut kctx = crate::compile::KernelCtx::new(); let classes = vec![vec![a.clone(), b.clone()]]; let (recs, is_prop) = @@ -3846,8 +3844,8 @@ mod tests { #[test] fn test_aux_gen_below_def_type() { let (env, t) = build_type_nat_env(); - let stt = crate::ix::compile::CompileState::default(); - let mut kctx = crate::ix::compile::KernelCtx::new(); + let stt = crate::compile::CompileState::default(); + let mut kctx = crate::compile::KernelCtx::new(); let classes = vec![vec![t.clone()]]; let (recs, is_prop) = @@ -3888,8 +3886,8 @@ mod tests { #[test] fn test_aux_gen_is_prop_vs_is_large() { let (env, p) = build_prop_drec_env(); - let stt = crate::ix::compile::CompileState::default(); - let mut kctx = crate::ix::compile::KernelCtx::new(); + let stt = crate::compile::CompileState::default(); + let mut kctx = crate::compile::KernelCtx::new(); let classes = vec![vec![p.clone()]]; let (recs, is_prop) = @@ -3933,7 +3931,7 @@ mod tests { #[ignore] #[test] fn test_aux_gen_compile_roundtrip() { - use crate::ix::compile::env::compile_env; + use crate::compile::env::compile_env; use std::sync::Arc; let (mut env, a, b) = build_alpha_collapse_env(); @@ -3986,18 +3984,18 @@ mod tests { /// Type-level brecOn: Nat-like T generates .brecOn.go + .brecOn (no .eq without Eq in env). #[test] fn test_brecon_type_level() { - use crate::ix::compile::aux_gen::below::generate_below_constants; - use crate::ix::compile::aux_gen::brecon::generate_brecon_constants; + use crate::compile::aux_gen::below::generate_below_constants; + use crate::compile::aux_gen::brecon::generate_brecon_constants; let (env, t) = build_type_nat_env(); - let stt = crate::ix::compile::CompileState::default(); - let mut kctx = crate::ix::compile::KernelCtx::new(); + let stt = crate::compile::CompileState::default(); + let mut kctx = crate::compile::KernelCtx::new(); // Ingress prelude (PUnit, PProd) and the inductive into the kenv // so TcScope can resolve them during brecOn sort-level inference. - crate::ix::compile::aux_gen::expr_utils::ensure_prelude_in_kenv_of( + crate::compile::aux_gen::expr_utils::ensure_prelude_in_kenv_of( &stt, &mut kctx, ); - crate::ix::compile::aux_gen::expr_utils::ensure_in_kenv_of( + crate::compile::aux_gen::expr_utils::ensure_in_kenv_of( &t, &env, &stt, &mut kctx, ); @@ -4012,7 +4010,7 @@ mod tests { assert_eq!(below.len(), 1); // Populate kenv with .below types for brecOn generation. - crate::ix::compile::aux_gen::populate_canon_kenv_with_below( + crate::compile::aux_gen::populate_canon_kenv_with_below( &below, &classes, &std::sync::Arc::new(env.clone()), @@ -4046,12 +4044,12 @@ mod tests { /// Prop-level brecOn: alpha-collapse A/B generates single .brecOn per class. #[test] fn test_brecon_prop_alpha_collapse() { - use crate::ix::compile::aux_gen::below::generate_below_constants; - use crate::ix::compile::aux_gen::brecon::generate_brecon_constants; + use crate::compile::aux_gen::below::generate_below_constants; + use crate::compile::aux_gen::brecon::generate_brecon_constants; let (env, a, b) = build_alpha_collapse_env(); - let stt = crate::ix::compile::CompileState::default(); - let mut kctx = crate::ix::compile::KernelCtx::new(); + let stt = crate::compile::CompileState::default(); + let mut kctx = crate::compile::KernelCtx::new(); let classes = vec![vec![a.clone(), b.clone()]]; let (recs, is_prop) = @@ -4081,8 +4079,8 @@ mod tests { /// Non-recursive inductives should NOT generate brecOn. #[test] fn test_brecon_skipped_for_non_recursive() { - use crate::ix::compile::aux_gen::below::generate_below_constants; - use crate::ix::compile::aux_gen::brecon::generate_brecon_constants; + use crate::compile::aux_gen::below::generate_below_constants; + use crate::compile::aux_gen::brecon::generate_brecon_constants; // Build a simple non-recursive inductive: Unit | unit : Unit let unit = n("Unit"); @@ -4122,8 +4120,8 @@ mod tests { }), ); - let stt = crate::ix::compile::CompileState::default(); - let mut kctx = crate::ix::compile::KernelCtx::new(); + let stt = crate::compile::CompileState::default(); + let mut kctx = crate::compile::KernelCtx::new(); let classes = vec![vec![unit]]; let (recs, is_prop) = generate_canonical_recursors(&classes, &env, &stt, &mut kctx).unwrap(); @@ -4150,7 +4148,7 @@ mod tests { /// with real Lean environments that include .below and .brecOn constants. #[test] fn test_brecon_type_compile_roundtrip() { - use crate::ix::compile::env::compile_env; + use crate::compile::env::compile_env; use std::sync::Arc; let (mut env, t) = build_type_nat_env(); diff --git a/src/ix/compile/env.rs b/crates/compile/src/compile/env.rs similarity index 98% rename from src/ix/compile/env.rs rename to crates/compile/src/compile/env.rs index 7f0731b8f..0b36bc233 100644 --- a/src/ix/compile/env.rs +++ b/crates/compile/src/compile/env.rs @@ -14,15 +14,15 @@ use dashmap::DashMap; use rayon::prelude::*; use rustc_hash::FxHashSet; -use crate::ix::address::Address; -use crate::ix::compile::{ +use crate::compile::{ BlockCache, CompileOptions, CompileState, compile_const, compile_const_no_aux, }; -use crate::ix::condense::compute_sccs; -use crate::ix::env::{Env as LeanEnv, Name}; -use crate::ix::graph::{NameSet, build_ref_graph}; -use crate::ix::ground::ground_consts; -use crate::ix::ixon::CompileError; +use crate::condense::compute_sccs; +use crate::graph::{NameSet, build_ref_graph}; +use crate::ground::ground_consts; +use ix_common::address::Address; +use ix_common::env::{Env as LeanEnv, Name}; +use ixon::CompileError; // =========================================================================== // Progress + diagnostic logging @@ -150,8 +150,7 @@ pub fn compile_env_with_options( // Filter ungrounded names from the ref graph before SCC computation so // condensed blocks only contain constants we can actually compile. - let grounded_out_refs: crate::ix::graph::RefMap = if ungrounded_map.is_empty() - { + let grounded_out_refs: crate::graph::RefMap = if ungrounded_map.is_empty() { graph.out_refs } else { graph @@ -455,7 +454,7 @@ pub fn compile_env_with_options( // Spawn worker threads for _ in 0..num_threads { s.spawn(move || { - let mut worker_kctx = crate::ix::compile::KernelCtx::new(); + let mut worker_kctx = crate::compile::KernelCtx::new(); loop { // Try to get work from the ready queue let work = { @@ -742,7 +741,7 @@ pub fn compile_env_with_options( // Check for slow blocks let elapsed = block_start.elapsed(); - if *crate::ix::compile::IX_TIMING && elapsed.as_secs_f32() > 1.0 { + if *crate::compile::IX_TIMING && elapsed.as_secs_f32() > 1.0 { let cc_time = _cc_start.elapsed().as_secs_f32(); eprintln!( "Slow block {:?} ({} consts): {:.2}s path={} cc={:.2}s", @@ -956,7 +955,7 @@ fn aux_gen_seed_names() -> Vec { /// references; we resolve each referenced name back to its own SCC-rep via /// `condensed.low_links`. fn precompile_aux_gen_prereqs( - condensed: &crate::ix::condense::CondensedBlocks, + condensed: &crate::condense::CondensedBlocks, lean_env: &Arc, stt: &CompileState, ) -> Result<(), CompileError> { @@ -1011,7 +1010,7 @@ fn precompile_aux_gen_prereqs( // Compile each SCC in dep-first order, moving compiled names to // `aux_name_to_addr` so later SCCs can resolve their Const refs. - let mut prereq_kctx = crate::ix::compile::KernelCtx::new(); + let mut prereq_kctx = crate::compile::KernelCtx::new(); for rep in order { if stt.aux_name_to_addr.contains_key(&rep) { continue; // Already compiled (e.g., via a prior prereq run). diff --git a/src/ix/compile/mutual.rs b/crates/compile/src/compile/mutual.rs similarity index 97% rename from src/ix/compile/mutual.rs rename to crates/compile/src/compile/mutual.rs index fa7ef7d95..68bbca19b 100644 --- a/src/ix/compile/mutual.rs +++ b/crates/compile/src/compile/mutual.rs @@ -15,23 +15,24 @@ use std::sync::Arc; use rustc_hash::FxHashMap; -use lean_ffi::nat::Nat; - -use crate::ix::address::Address; -use crate::ix::compile::aux_gen::below::BelowIndc; -use crate::ix::compile::aux_gen::brecon::BRecOnDef; -use crate::ix::compile::aux_gen::recursor; -use crate::ix::compile::aux_gen::{self, PatchedConstant}; -use crate::ix::compile::{ +use bignat::Nat; + +use crate::compile::aux_gen::below::BelowIndc; +use crate::compile::aux_gen::brecon::BRecOnDef; +use crate::compile::aux_gen::recursor; +use crate::compile::aux_gen::{self, PatchedConstant}; +use crate::compile::{ BlockCache, CompileState, collect_mut_const_exprs, compile_definition, compile_inductive, compile_mutual_block, compile_name, compile_recursor, preseed_expr_tables, sort_consts, }; -use crate::ix::env::{ +use crate::mutual::{Def, Ind, MutConst}; +use ix_common::address::Address; +use ix_common::env::{ ConstantInfo as LeanConstantInfo, ConstantVal, ConstructorVal, DefinitionSafety, Env as LeanEnv, Name, ReducibilityHints, }; -use crate::ix::ixon::{ +use ixon::{ CompileError, constant::{ Constant, DefKind, MutConst as IxonMutConst, ctor_proj_constant, @@ -41,7 +42,6 @@ use crate::ix::ixon::{ metadata::{ConstantMeta, ConstantMetaInfo}, univ::Univ, }; -use crate::ix::mutual::{Def, Ind, MutConst}; // =========================================================================== // compile_aux_block @@ -57,11 +57,11 @@ use crate::ix::mutual::{Def, Ind, MutConst}; /// `stt.name_to_addr`) so they don't interfere with the scheduler's /// dependency tracking. The scheduler's promotion path in `env.rs` moves /// them to `name_to_addr` when the block is processed. -pub(crate) fn compile_aux_block( +pub fn compile_aux_block( aux_consts: &[MutConst], lean_env: &Arc, stt: &CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + kctx: &mut crate::compile::KernelCtx, ) -> Result<(), CompileError> { compile_aux_block_with_rename(aux_consts, lean_env, stt, kctx, None, None) } @@ -95,16 +95,16 @@ pub(crate) fn compile_aux_block( /// Without this reorder, `sort_consts` on recursors picks an independent /// canonical permutation that diverges from the inductive block's layout. /// See `docs/ix_canonicity.md` §6.2 and the rationale in -/// `kernel::inductive::populate_recursor_rules_from_block`. +/// `ix_kernel::inductive::populate_recursor_rules_from_block`. /// /// The class ordering produced by `sort_consts` is preserved as a /// stable tiebreak: classes that map to `u64::MAX` (no key entry) keep /// their `sort_consts` relative position at the tail. -pub(crate) fn compile_aux_block_with_rename( +pub fn compile_aux_block_with_rename( aux_consts: &[MutConst], lean_env: &Arc, stt: &CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + kctx: &mut crate::compile::KernelCtx, name_rename: Option<&FxHashMap>, class_order_key: Option<&dyn Fn(&MutConst) -> u64>, ) -> Result<(), CompileError> { @@ -199,7 +199,7 @@ pub(crate) fn compile_aux_block_with_rename( if ixon_mutuals.len() == 1 && !matches!(&ixon_mutuals[0], IxonMutConst::Indc(_)) { - use crate::ix::compile::{ + use crate::compile::{ apply_sharing_to_definition_with_stats, apply_sharing_to_recursor_with_stats, }; @@ -508,13 +508,13 @@ fn content_address(constant: &Constant) -> Address { /// /// Only runs for inductive blocks. Non-inductive mutual blocks return /// immediately. -pub(crate) fn generate_and_compile_aux_recursors( +pub fn generate_and_compile_aux_recursors( cs: &[MutConst], class_names: &[Vec], lean_env: &Arc, stt: &CompileState, - kctx: &mut crate::ix::compile::KernelCtx, -) -> Result, CompileError> { + kctx: &mut crate::compile::KernelCtx, +) -> Result, CompileError> { // Guard: aux_gen canonical generation only runs for blocks containing // inductives. Non-inductive blocks (plain defs, recursor-only SCCs, // etc.) have no canonical auxiliaries to generate. @@ -603,7 +603,7 @@ pub(crate) fn generate_and_compile_aux_recursors( // `original_all` (= `source_all` above) is hoisted to the enclosing // scope so the aux-name rename map construction below can reuse it. let original_all: Vec = source_all; - let mut aux_layout: Option = None; + let mut aux_layout: Option = None; if !original_all.is_empty() && let Some(perm) = aux_out.perm.clone() && !perm.is_empty() @@ -639,7 +639,7 @@ pub(crate) fn generate_and_compile_aux_recursors( }); } aux_layout = - Some(crate::ix::compile::surgery::AuxLayout { perm, source_ctor_counts }); + Some(crate::compile::surgery::AuxLayout { perm, source_ctor_counts }); } // NOTE: Historically, a canonical→source rename map was built here @@ -889,7 +889,7 @@ pub(crate) fn generate_and_compile_aux_recursors( // blocks). See the aux_gen.rs module docs for the full rationale. let total = aux_total_start.elapsed(); - if *crate::ix::compile::IX_TIMING && total.as_secs_f32() > 0.5 { + if *crate::compile::IX_TIMING && total.as_secs_f32() > 0.5 { eprintln!( "[aux_gen] {:?} total={:.2}s gen={:.2}s rec={:.2}s cases={:.2}s recOn={:.2}s below={:.2}s belowRec={:.2}s brecon={:.2}s patches={}", block_label, @@ -939,7 +939,7 @@ fn below_indc_to_mut_const( .collect(); MutConst::Indc(Ind { - ind: crate::ix::env::InductiveVal { + ind: ix_common::env::InductiveVal { cnst: ConstantVal { name: bi.name.clone(), level_params: bi.level_params.clone(), @@ -1054,7 +1054,7 @@ fn compile_below_recursors( below_indcs: &[MutConst], lean_env: &Arc, stt: &CompileState, - kctx: &mut crate::ix::compile::KernelCtx, + kctx: &mut crate::compile::KernelCtx, ) -> Result<(), CompileError> { // Build a small overlay with just the .below inductives + ctors. // These don't exist in the original lean_env, but generate_canonical_recursors diff --git a/src/ix/compile/nat_conv.rs b/crates/compile/src/compile/nat_conv.rs similarity index 82% rename from src/ix/compile/nat_conv.rs rename to crates/compile/src/compile/nat_conv.rs index b1a2be228..a96ec090b 100644 --- a/src/ix/compile/nat_conv.rs +++ b/crates/compile/src/compile/nat_conv.rs @@ -5,14 +5,14 @@ //! `num_nested`) are always small values. These utilities make the conversion //! explicit rather than silently producing 0 on overflow. -use lean_ffi::nat::Nat; +use bignat::Nat; -use crate::ix::ixon::CompileError; +use ixon::CompileError; /// Convert a Lean `Nat` to `usize`, returning `CompileError` on overflow. /// /// Use in functions that return `Result<_, CompileError>`. -pub(crate) fn try_nat_to_usize(n: &Nat) -> Result { +pub fn try_nat_to_usize(n: &Nat) -> Result { n.to_u64().map(|v| v as usize).ok_or_else(|| CompileError::UnsupportedExpr { desc: "Nat field exceeds u64".into(), }) @@ -23,11 +23,11 @@ pub(crate) fn try_nat_to_usize(n: &Nat) -> Result { /// Use in pure functions where returning `Result` would cascade through /// callers. Overflow is impossible for valid Lean metadata — these fields /// represent type constructor arities which are always < 2^64. -pub(crate) fn nat_to_usize(n: &Nat) -> usize { +pub fn nat_to_usize(n: &Nat) -> usize { n.to_u64().expect("Nat field exceeds u64") as usize } /// Convert a Lean `Nat` to `u64`, panicking on overflow. -pub(crate) fn nat_to_u64(n: &Nat) -> u64 { +pub fn nat_to_u64(n: &Nat) -> u64 { n.to_u64().expect("Nat field exceeds u64") } diff --git a/src/ix/compile/surgery.rs b/crates/compile/src/compile/surgery.rs similarity index 98% rename from src/ix/compile/surgery.rs rename to crates/compile/src/compile/surgery.rs index fb204f14c..94a5029d0 100644 --- a/src/ix/compile/surgery.rs +++ b/crates/compile/src/compile/surgery.rs @@ -23,12 +23,12 @@ use std::sync::Arc; use rustc_hash::FxHashMap; -use crate::ix::env::{ +use ix_common::env::{ ConstantInfo as LeanConstantInfo, ConstructorVal, Env as LeanEnv, Expr as LeanExpr, ExprData, Level, Name, NameData, RecursorVal, }; -use crate::ix::ixon::error::CompileError; -use crate::ix::ixon::expr::Expr as IxonExpr; +use ixon::error::CompileError; +use ixon::expr::Expr as IxonExpr; use super::{ aux_gen::expr_utils::{ @@ -151,7 +151,7 @@ impl BRecOnCallSitePlan { } } -pub(crate) fn rec_name_to_brecon_name(name: &Name) -> Option { +pub fn rec_name_to_brecon_name(name: &Name) -> Option { match name.as_data() { NameData::Str(parent, s, _) if s == "rec" => { Some(Name::str(parent.clone(), "brecOn".to_string())) @@ -163,7 +163,7 @@ pub(crate) fn rec_name_to_brecon_name(name: &Name) -> Option { } } -pub(crate) fn rec_name_to_below_name(name: &Name) -> Option { +pub fn rec_name_to_below_name(name: &Name) -> Option { match name.as_data() { NameData::Str(parent, s, _) if s == "rec" => { Some(Name::str(parent.clone(), "below".to_string())) @@ -182,7 +182,7 @@ pub(crate) fn rec_name_to_below_name(name: &Name) -> Option { /// Collect a Lean App telescope: peel App nodes to get `(head, [a1, ..., aN])`. /// /// Arguments are returned in application order (leftmost first). -pub(crate) fn collect_lean_telescope<'a>( +pub fn collect_lean_telescope<'a>( e: &'a LeanExpr, ) -> (&'a LeanExpr, Vec<&'a LeanExpr>) { let mut args: Vec<&'a LeanExpr> = Vec::new(); @@ -199,7 +199,7 @@ pub(crate) fn collect_lean_telescope<'a>( /// /// Arguments are returned in application order (leftmost first). #[allow(dead_code)] -pub(crate) fn collect_ixon_telescope( +pub fn collect_ixon_telescope( e: &Arc, ) -> (Arc, Vec>) { let mut args: Vec> = Vec::new(); @@ -237,14 +237,14 @@ pub(crate) fn collect_ixon_telescope( /// its own plan). We skip generating a plan for a phantom `X.rec` /// itself, since that belongs to the block owning `X`. /// -/// The [`AuxLayout`] type is re-exported from `crate::ix::ixon::env` so it +/// The [`AuxLayout`] type is re-exported from `ixon::env` so it /// can live in the Ixon env side-table and survive serialization — see the -/// doc on [`crate::ix::ixon::env::AuxLayout`] for the canonical definition. -pub(crate) use crate::ix::ixon::env::AuxLayout; +/// doc on [`ixon::env::AuxLayout`] for the canonical definition. +pub use ixon::env::AuxLayout; const PERM_OUT_OF_SCC: usize = usize::MAX; -pub(crate) fn compute_call_site_plans( +pub fn compute_call_site_plans( sorted_classes: &[Vec], original_all: &[Name], lean_env: &LeanEnv, @@ -706,7 +706,7 @@ pub(crate) fn compute_call_site_plans( /// recursor call then goes through the normal call-site surgery for its own /// SCC. #[allow(clippy::too_many_arguments)] -pub(crate) fn adapt_split_minor( +pub fn adapt_split_minor( rec_name: &Name, rec_levels: &[Level], plan: &CallSitePlan, @@ -1115,8 +1115,8 @@ fn dump_plan_state( #[cfg(test)] mod tests { use super::*; - use crate::ix::env::{ConstantVal, ConstructorVal, InductiveVal}; - use lean_ffi::nat::Nat; + use bignat::Nat; + use ix_common::env::{ConstantVal, ConstructorVal, InductiveVal}; fn n(s: &str) -> Name { Name::str(Name::anon(), s.to_string()) @@ -1208,8 +1208,8 @@ mod tests { fn build_test_env( names: &[&str], ctor_counts: &[usize], - ) -> crate::ix::env::Env { - let mut env = crate::ix::env::Env::default(); + ) -> ix_common::env::Env { + let mut env = ix_common::env::Env::default(); let all: Vec = names.iter().map(|s| n(s)).collect(); for (i, &name_str) in names.iter().enumerate() { @@ -1398,7 +1398,7 @@ mod tests { ctor_counts: &[usize], aux_motives: usize, aux_minors: usize, - ) -> crate::ix::env::Env { + ) -> ix_common::env::Env { let mut env = build_test_env(names, ctor_counts); // Overwrite each inductive's recursor with inflated motive/minor counts. let total_motives = (names.len() + aux_motives) as u64; diff --git a/src/ix/condense.rs b/crates/compile/src/condense.rs similarity index 98% rename from src/ix/condense.rs rename to crates/compile/src/condense.rs index ab0a6b273..f293aaa04 100644 --- a/src/ix/condense.rs +++ b/crates/compile/src/condense.rs @@ -5,11 +5,10 @@ use rustc_hash::FxHashMap; -use crate::{ - FxIndexSet, - ix::env::Name, - ix::graph::{NameSet, RefMap}, -}; +use ix_common::FxIndexSet; +use ix_common::env::Name; + +use crate::graph::{NameSet, RefMap}; /// The condensation of a reference graph into strongly connected components. pub struct CondensedBlocks { diff --git a/src/ix/congruence.rs b/crates/compile/src/congruence.rs similarity index 98% rename from src/ix/congruence.rs rename to crates/compile/src/congruence.rs index 4f0750204..c74c2ee32 100644 --- a/src/ix/congruence.rs +++ b/crates/compile/src/congruence.rs @@ -14,8 +14,8 @@ pub mod perm; -use crate::ix::env::{ConstantInfo, Expr, ExprData, Level, LevelData, Literal}; -use lean_ffi::nat::Nat; +use bignat::Nat; +use ix_common::env::{ConstantInfo, Expr, ExprData, Level, LevelData, Literal}; /// Check that two Lean levels are equal modulo the same simplifications /// `Level::max_smart` / `Level::imax_smart` perform. @@ -334,7 +334,7 @@ pub fn const_alpha_eq( // ========================================================================= /// Strip Mdata wrappers from an expression. -pub(crate) fn strip_mdata(e: &Expr) -> &Expr { +pub fn strip_mdata(e: &Expr) -> &Expr { let mut cur = e; while let ExprData::Mdata(_, inner, _) = cur.as_data() { cur = inner; @@ -342,11 +342,7 @@ pub(crate) fn strip_mdata(e: &Expr) -> &Expr { cur } -pub(crate) fn check_nat_eq( - a: &Nat, - b: &Nat, - field: &str, -) -> Result<(), String> { +pub fn check_nat_eq(a: &Nat, b: &Nat, field: &str) -> Result<(), String> { let av = a.to_u64().unwrap_or(u64::MAX); let bv = b.to_u64().unwrap_or(u64::MAX); if av != bv { @@ -411,7 +407,7 @@ mod tests { //! `Level::imax_smart` (see `src/ix/env.rs:340-404`), so they double //! as a contract test for those constructors. use super::*; - use crate::ix::env::Name; + use ix_common::env::Name; fn p(s: &str) -> Level { Level::param(Name::str(Name::anon(), s.to_string())) } diff --git a/src/ix/congruence/perm.rs b/crates/compile/src/congruence/perm.rs similarity index 99% rename from src/ix/congruence/perm.rs rename to crates/compile/src/congruence/perm.rs index 3def435be..f179c39ef 100644 --- a/src/ix/congruence/perm.rs +++ b/crates/compile/src/congruence/perm.rs @@ -65,19 +65,16 @@ //! - `InductInfo`, `CtorInfo`, `AxiomInfo`, `QuotInfo` — pass-through //! (no permutation needed). -use lean_ffi::nat::Nat; +use bignat::Nat; use rustc_hash::FxHashMap; -use crate::ix::compile::aux_gen::expr_utils::{ - forall_telescope, lambda_telescope, -}; -use crate::ix::{ - address::Address, - env::{ - ConstantInfo, ConstantVal, Expr, ExprData, Name, RecursorRule, RecursorVal, - }, +use ix_common::address::Address; +use ix_common::env::{ + ConstantInfo, ConstantVal, Expr, ExprData, Name, RecursorRule, RecursorVal, }; +use crate::compile::aux_gen::expr_utils::{forall_telescope, lambda_telescope}; + use super::{check_nat_eq, expr_alpha_eq, level_alpha_eq, strip_mdata}; /// Sentinel for `aux_perm` entries that don't correspond to any canonical @@ -352,7 +349,7 @@ impl PermCtx { /// counterparts. Built once per binder telescope, passed by shared /// reference into the alpha-eq walk. #[derive(Default, Clone)] -pub(crate) struct Corr { +pub struct Corr { fvar_map: FxHashMap, fvar_alts: FxHashMap>, punit_motive_gen: Vec, @@ -538,7 +535,7 @@ fn classify_defn_shape(name: &Name) -> DefnShape { /// Collect up to `n` trailing `Str` segments of `name`, from leaf /// outward. `Num` segments or `Anonymous` terminate collection early. fn collect_name_tail_strs(name: &Name, n: usize) -> Vec { - use crate::ix::env::NameData; + use ix_common::env::NameData; let mut out: Vec = Vec::with_capacity(n); let mut cur = name.clone(); for _ in 0..n { @@ -1273,8 +1270,8 @@ fn n_canonical_minors_of(ctx: &PermCtx) -> usize { fn add_motive_alts( corr: &mut Corr, ctx: &PermCtx, - orig_decls: &[crate::ix::compile::aux_gen::expr_utils::LocalDecl], - gen_decls: &[crate::ix::compile::aux_gen::expr_utils::LocalDecl], + orig_decls: &[crate::compile::aux_gen::expr_utils::LocalDecl], + gen_decls: &[crate::compile::aux_gen::expr_utils::LocalDecl], ) { let n_params = ctx.n_params; let n_source_motives = ctx.n_source_motives(); @@ -1353,7 +1350,7 @@ fn is_motive_app(e: &Expr, motives: &[Name]) -> bool { /// - `LetE` / `Proj` / `Mdata`: recurse; `Mdata` is stripped before /// matching so it's essentially a no-op. /// - `Sort`, `Lit`: compare literally. -pub(crate) fn expr_alpha_eq_ctx( +pub fn expr_alpha_eq_ctx( g: &Expr, orig: &Expr, ctx: &PermCtx, @@ -1800,9 +1797,8 @@ fn peel_all_lambdas( expr: &Expr, prefix: &str, min_count: usize, -) -> (Vec, Vec, Expr) -{ - use crate::ix::compile::aux_gen::expr_utils::LocalDecl; +) -> (Vec, Vec, Expr) { + use crate::compile::aux_gen::expr_utils::LocalDecl; let (mut fvars, mut decls, mut body): (Vec, Vec, Expr) = if min_count == 0 { diff --git a/src/ix/decompile.rs b/crates/compile/src/decompile.rs similarity index 98% rename from src/ix/decompile.rs rename to crates/compile/src/decompile.rs index 3a954a7b0..6f5edcc33 100644 --- a/src/ix/decompile.rs +++ b/crates/compile/src/decompile.rs @@ -9,35 +9,37 @@ #![allow(clippy::map_err_ignore)] #![allow(clippy::match_same_arms)] -use lean_ffi::nat::Nat; +use bignat::Nat; + +use ix_common::address::Address; +use ix_common::env::{ + AxiomVal, BinderInfo, ConstantInfo as LeanConstantInfo, ConstantVal, + ConstructorVal, DataValue as LeanDataValue, DefinitionSafety, DefinitionVal, + Env as LeanEnv, Expr as LeanExpr, InductiveVal, Int, Level, Literal, Name, + OpaqueVal, QuotVal, RecursorRule as LeanRecursorRule, RecursorVal, + ReducibilityHints, SourceInfo, Substring, Syntax, SyntaxPreresolved, + TheoremVal, +}; -use crate::{ - ix::address::Address, - ix::compile::CompileState, - ix::env::{ - AxiomVal, BinderInfo, ConstantInfo as LeanConstantInfo, ConstantVal, - ConstructorVal, DataValue as LeanDataValue, DefinitionSafety, - DefinitionVal, Env as LeanEnv, Expr as LeanExpr, InductiveVal, Int, Level, - Literal, Name, OpaqueVal, QuotVal, RecursorRule as LeanRecursorRule, - RecursorVal, ReducibilityHints, SourceInfo, Substring, Syntax, - SyntaxPreresolved, TheoremVal, +use ixon::{ + DecompileError, Tag0, + constant::{ + Axiom, Constant, ConstantInfo, Constructor, DefKind, Definition, + DefinitionProj, Inductive, InductiveProj, MutConst, Quotient, Recursor, + RecursorProj, }, - ix::ixon::{ - DecompileError, Tag0, - constant::{ - Axiom, Constant, ConstantInfo, Constructor, DefKind, Definition, - DefinitionProj, Inductive, InductiveProj, MutConst, Quotient, Recursor, - RecursorProj, - }, - env::Named, - expr::Expr, - metadata::{ - CallSiteEntry, ConstantMeta, ConstantMetaInfo, DataValue, ExprMeta, - ExprMetaData, KVMap, - }, - univ::Univ, + env::Named, + expr::Expr, + metadata::{ + CallSiteEntry, ConstantMeta, ConstantMetaInfo, DataValue, ExprMeta, + ExprMetaData, KVMap, }, - ix::mutual::{Def, Ind, MutConst as LeanMutConst, MutCtx, all_to_ctx}, + univ::Univ, +}; + +use crate::{ + compile::CompileState, + mutual::{Def, Ind, MutConst as LeanMutConst, MutCtx, all_to_ctx}, }; use dashmap::DashMap; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; @@ -1913,14 +1915,14 @@ enum AuxKind { /// we're going to regenerate anyway should never have its call-sites /// surgered, since the regenerated body is emitted in canonical order /// by construction. -pub(crate) fn is_aux_gen_suffix(name: &Name) -> bool { +pub fn is_aux_gen_suffix(name: &Name) -> bool { classify_aux_gen(name).is_some() } /// Classify an aux_gen constant by suffix, returning (kind, root_inductive). /// The root inductive is the base inductive the auxiliary is derived from. fn classify_aux_gen(name: &Name) -> Option<(AuxKind, Name)> { - use crate::ix::env::NameData; + use ix_common::env::NameData; let s1 = name.last_str()?; let p1 = match name.as_data() { NameData::Str(parent, _, _) => parent.clone(), @@ -2015,7 +2017,7 @@ fn def_safety(is_unsafe: bool) -> DefinitionSafety { /// `mkDefinitionValInferringUnsafe`, which always flips to `Unsafe` when the /// parent inductive is unsafe (the value references the parent's `.rec`). fn below_def_to_lean( - def: &crate::ix::compile::aux_gen::below::BelowDef, + def: &crate::compile::aux_gen::below::BelowDef, ) -> LeanConstantInfo { LeanConstantInfo::DefnInfo(DefinitionVal { cnst: ConstantVal { @@ -2036,7 +2038,7 @@ fn below_def_to_lean( /// branch of `IndPredBelow`). The constructor `is_unsafe` matches the /// enclosing inductive — the kernel rejects mixed safety within an inductive. fn below_indc_to_lean( - indc: &crate::ix::compile::aux_gen::below::BelowIndc, + indc: &crate::compile::aux_gen::below::BelowIndc, all_below_names: &[Name], ) -> (InductiveVal, Vec) { let ctor_names: Vec = @@ -2096,7 +2098,7 @@ fn below_indc_to_lean( /// (`Lean/Environment.lean:2797`), which replaces a theorem with an unsafe /// definition whenever `env.hasUnsafe` fires on the type or value. fn brecon_def_to_lean( - def: &crate::ix::compile::aux_gen::brecon::BRecOnDef, + def: &crate::compile::aux_gen::brecon::BRecOnDef, ) -> LeanConstantInfo { let cnst = ConstantVal { name: def.name.clone(), @@ -2408,11 +2410,11 @@ fn roundtrip_block( stt: &CompileState, dstt: &DecompileState, ) -> Result, DecompileError> { - use crate::ix::compile::{ + use crate::compile::{ BlockCache as CompileBlockCache, compile_definition, compile_inductive, compile_mutual_block, compile_recursor, sort_consts, }; - use crate::ix::mutual::ctx_to_all; + use crate::mutual::ctx_to_all; let mut results: FxHashMap = FxHashMap::default(); if consts.is_empty() { @@ -2507,7 +2509,7 @@ fn roundtrip_block( // Singleton: compile as bare constant (no Muts wrapper). let result = match &ixon_mutuals[0] { MutConst::Defn(def) => { - crate::ix::compile::apply_sharing_to_definition_with_stats( + crate::compile::apply_sharing_to_definition_with_stats( def.clone(), block_refs, block_univs, @@ -2515,7 +2517,7 @@ fn roundtrip_block( ) }, MutConst::Recr(rec) => { - crate::ix::compile::apply_sharing_to_recursor_with_stats( + crate::compile::apply_sharing_to_recursor_with_stats( rec.clone(), block_refs, block_univs, @@ -3182,7 +3184,7 @@ fn decompile_named_const( /// Idempotent: if `stt.aux_perms` already has an entry for the name, we /// leave it alone (compile-in-progress stt wins over rehydrated copy). fn rehydrate_aux_perms_from_env(stt: &CompileState) { - use crate::ix::ixon::metadata::ConstantMetaInfo; + use ixon::metadata::ConstantMetaInfo; let mut n_muts = 0usize; let mut n_muts_with_layout = 0usize; @@ -3294,7 +3296,7 @@ fn block_mut_consts_from_env( #[derive(Clone)] struct StoredPlanBlock { class_names: Vec>, - aux_layout: Option, + aux_layout: Option, flat_names: Vec, } @@ -3390,7 +3392,7 @@ fn fallback_plan_blocks_from_sort( env: &LeanEnv, stt: &CompileState, ) -> Result, DecompileError> { - use crate::ix::compile::{BlockCache as CompileBlockCache, sort_consts}; + use crate::compile::{BlockCache as CompileBlockCache, sort_consts}; let cs = block_mut_consts_from_env(all_names, env)?; if cs.is_empty() { @@ -3422,7 +3424,7 @@ fn install_decompile_call_site_plans( env: &LeanEnv, stt: &CompileState, ) -> Result<(), DecompileError> { - use crate::ix::compile::{aux_gen, surgery}; + use crate::compile::{aux_gen, surgery}; if all_names.is_empty() { return Ok(()); @@ -3502,11 +3504,11 @@ fn decompile_block_aux_gen( all_names: &[Name], aux_members: &[(AuxKind, Name)], env: &mut LeanEnv, - kctx: &mut crate::ix::compile::KernelCtx, + kctx: &mut crate::compile::KernelCtx, stt: &CompileState, dstt: &DecompileState, ) -> Vec<(Name, DecompileError)> { - use crate::ix::compile::aux_gen::{ + use crate::compile::aux_gen::{ below::{BelowConstant, generate_below_constants}, brecon::generate_brecon_constants, cases_on::generate_cases_on, @@ -3541,7 +3543,7 @@ fn decompile_block_aux_gen( // Ingress transitive dependencies from constructor field types. { - use crate::ix::graph::get_constant_info_references; + use crate::graph::get_constant_info_references; for ind_name in all_names { if let Some(ci) = env.get(ind_name) { for ref_name in get_constant_info_references(ci) { @@ -3587,7 +3589,7 @@ fn decompile_block_aux_gen( // // See `docs/ix_canonicity.md` §9.3 / §17.2 for the canonicity // commitment this upholds. - let aux_layout_for_block: Option = None; + let aux_layout_for_block: Option = None; let (canonical_recs, is_prop) = if needs_rec || needs_rec_on @@ -3596,7 +3598,7 @@ fn decompile_block_aux_gen( || needs_below_rec || needs_brecon { - match crate::ix::compile::aux_gen::recursor::generate_canonical_recursors_with_layout( + match crate::compile::aux_gen::recursor::generate_canonical_recursors_with_layout( &classes, env, None, None, stt, kctx, aux_layout_for_block.as_ref(), None, // source_of_canonical derived from aux_layout inside _with_layout @@ -3697,7 +3699,7 @@ fn decompile_block_aux_gen( for co_name in &cases_on_members { let ind_name = match co_name.as_data() { - crate::ix::env::NameData::Str(parent, _, _) => parent.clone(), + ix_common::env::NameData::Str(parent, _, _) => parent.clone(), _ => continue, }; let rec_name = Name::str(ind_name.clone(), "rec".to_string()); @@ -3768,7 +3770,7 @@ fn decompile_block_aux_gen( // Phase 1c: Generate .recOn definitions (arg-reordered .rec wrapper). if needs_rec_on { - use crate::ix::compile::aux_gen::rec_on::generate_rec_on; + use crate::compile::aux_gen::rec_on::generate_rec_on; let rec_on_members: Vec<&Name> = aux_members .iter() @@ -3778,7 +3780,7 @@ fn decompile_block_aux_gen( for ro_name in &rec_on_members { let ind_name = match ro_name.as_data() { - crate::ix::env::NameData::Str(parent, _, _) => parent.clone(), + ix_common::env::NameData::Str(parent, _, _) => parent.clone(), _ => continue, }; let rec_name = Name::str(ind_name, "rec".to_string()); @@ -4233,8 +4235,7 @@ fn decompile_block_aux_gen( if let Some(orig) = orig_env { for (name, generated_ci) in &generated_consts { if let Some(orig_ci) = orig.get(name) - && let Err(e) = - crate::ix::congruence::const_alpha_eq(generated_ci, orig_ci) + && let Err(e) = crate::congruence::const_alpha_eq(generated_ci, orig_ci) { aux_gen_errors.push(( name.clone(), @@ -4262,10 +4263,10 @@ fn decompile_block_aux_gen( pub fn decompile_env( stt: &CompileState, ) -> Result { - use crate::ix::compile::KernelCtx; - use crate::ix::compile::aux_gen::expr_utils; - use crate::ix::condense::compute_sccs; - use crate::ix::graph::{NameSet, RefMap, get_constant_info_references}; + use crate::compile::KernelCtx; + use crate::compile::aux_gen::expr_utils; + use crate::condense::compute_sccs; + use crate::graph::{NameSet, RefMap, get_constant_info_references}; let dstt = DecompileState::default(); @@ -4660,8 +4661,8 @@ pub fn check_decompile( #[cfg(test)] mod tests { use super::*; - use crate::ix::compile::compile_name; - use crate::ix::env::Level; + use crate::compile::compile_name; + use ix_common::env::Level; /// Register a Name in `stt.env.names` so `decompile_name` can resolve it. /// Mirrors `compile_name` (content-address the name, insert into names map). @@ -4674,7 +4675,7 @@ mod tests { fn lean_telescope(e: &LeanExpr) -> (LeanExpr, Vec) { let mut args = Vec::new(); let mut cur = e.clone(); - while let crate::ix::env::ExprData::App(f, a, _) = cur.as_data() { + while let ix_common::env::ExprData::App(f, a, _) = cur.as_data() { args.push(a.clone()); cur = f.clone(); } @@ -4685,7 +4686,7 @@ mod tests { /// Pull the bvar index out of a Lean expr, or None if it isn't a bvar. fn bvar_idx(e: &LeanExpr) -> Option { match e.as_data() { - crate::ix::env::ExprData::Bvar(n, _) => n.to_u64(), + ix_common::env::ExprData::Bvar(n, _) => n.to_u64(), _ => None, } } @@ -4773,7 +4774,7 @@ mod tests { // The reconstructed spine should be in *source* order: Var 10, 11, 12. let (head_lean, args) = lean_telescope(&decompiled); match head_lean.as_data() { - crate::ix::env::ExprData::Const(name, _, _) => { + ix_common::env::ExprData::Const(name, _, _) => { assert_eq!(*name, head_name, "head const name mismatch"); }, other => panic!("expected Const head, got {other:?}"), @@ -4872,7 +4873,7 @@ mod tests { // Expected source-order spine: App(App(head, motive_ref), major). let (head_lean, args) = lean_telescope(&decompiled); match head_lean.as_data() { - crate::ix::env::ExprData::Const(name, _, _) => { + ix_common::env::ExprData::Const(name, _, _) => { assert_eq!(*name, head_name); }, other => panic!("expected head Const, got {other:?}"), @@ -4885,7 +4886,7 @@ mod tests { // args[0] is the collapsed motive — must be Const(target), NOT the // decoy lambda from sharing[0]. match args[0].as_data() { - crate::ix::env::ExprData::Const(name, _, _) => { + ix_common::env::ExprData::Const(name, _, _) => { assert_eq!( *name, target_name, "args[0] is the Collapsed motive and must resolve via \ @@ -4921,9 +4922,9 @@ mod tests { // ------------------------------------------------------------------------- #[test] fn test_projection_decompile_loads_meta_extensions() { - use crate::ix::address::Address; - use crate::ix::env::DefinitionSafety; - use crate::ix::ixon::constant::{ + use ix_common::address::Address; + use ix_common::env::DefinitionSafety; + use ixon::constant::{ DefKind, Definition, DefinitionProj, MutConst as IxMutConst, }; @@ -5041,7 +5042,7 @@ mod tests { LeanConstantInfo::DefnInfo(dv) => { let (head_lean, args) = lean_telescope(&dv.value); match head_lean.as_data() { - crate::ix::env::ExprData::Const(name, _, _) => { + ix_common::env::ExprData::Const(name, _, _) => { assert_eq!( *name, head_name, "CallSite head should decode as `head`" @@ -5051,7 +5052,7 @@ mod tests { } assert_eq!(args.len(), 2, "CallSite had 2 entries -> 2 app args"); match args[0].as_data() { - crate::ix::env::ExprData::Const(name, _, _) => { + ix_common::env::ExprData::Const(name, _, _) => { assert_eq!( *name, target_name, "Collapsed arg must resolve via loaded meta_sharing[0]" diff --git a/src/ix/graph.rs b/crates/compile/src/graph.rs similarity index 98% rename from src/ix/graph.rs rename to crates/compile/src/graph.rs index f90c7a953..e10bd777a 100644 --- a/src/ix/graph.rs +++ b/crates/compile/src/graph.rs @@ -9,7 +9,7 @@ use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use rustc_hash::{FxHashMap, FxHashSet}; use std::collections::hash_map::Entry; -use crate::ix::env::{ConstantInfo, Env, Expr, ExprData, Name}; +use ix_common::env::{ConstantInfo, Env, Expr, ExprData, Name}; /// A set of [`Name`]s, used to represent the neighbors of a node in the reference graph. pub type NameSet = FxHashSet; @@ -97,9 +97,7 @@ pub fn build_ref_graph(env: &Env) -> RefGraph { RefGraph { out_refs, in_refs } } -pub(crate) fn get_constant_info_references( - constant_info: &ConstantInfo, -) -> NameSet { +pub fn get_constant_info_references(constant_info: &ConstantInfo) -> NameSet { let cache = &mut FxHashMap::default(); match constant_info { ConstantInfo::AxiomInfo(val) => get_expr_references(&val.cnst.typ, cache), @@ -179,8 +177,8 @@ fn get_expr_references<'a>( #[cfg(test)] mod tests { use super::*; - use crate::ix::env::*; - use lean_ffi::nat::Nat; + use bignat::Nat; + use ix_common::env::*; fn n(s: &str) -> Name { Name::str(Name::anon(), s.to_string()) diff --git a/src/ix/ground.rs b/crates/compile/src/ground.rs similarity index 98% rename from src/ix/ground.rs rename to crates/compile/src/ground.rs index ffe7325a7..c854a3117 100644 --- a/src/ix/ground.rs +++ b/crates/compile/src/ground.rs @@ -9,15 +9,14 @@ use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use rustc_hash::{FxHashMap, FxHashSet}; use std::collections::hash_map::Entry; -use lean_ffi::nat::Nat; +use bignat::Nat; -use crate::{ - ix::env::{ - ConstantInfo, Env, Expr, ExprData, InductiveVal, Level, LevelData, Name, - }, - ix::graph::RefMap, +use ix_common::env::{ + ConstantInfo, Env, Expr, ExprData, InductiveVal, Level, LevelData, Name, }; +use crate::graph::RefMap; + /// Reason a constant failed groundedness checking. /// /// `Indc` carries `InductiveVal + Option` (~360 bytes) — the @@ -235,8 +234,8 @@ fn ground_level( #[cfg(test)] mod tests { use super::*; - use crate::ix::env::*; - use crate::ix::graph::build_ref_graph; + use crate::graph::build_ref_graph; + use ix_common::env::*; fn n(s: &str) -> Name { Name::str(Name::anon(), s.to_string()) diff --git a/src/ix/kernel/egress.rs b/crates/compile/src/kernel_egress.rs similarity index 98% rename from src/ix/kernel/egress.rs rename to crates/compile/src/kernel_egress.rs index 10435cfee..743f97db0 100644 --- a/src/ix/kernel/egress.rs +++ b/crates/compile/src/kernel_egress.rs @@ -7,20 +7,20 @@ use rayon::iter::{ }; use rustc_hash::FxHashMap; -use crate::ix::env::{ +use bignat::Nat; +use ix_common::env::{ self, AxiomVal, ConstantInfo as LeanCI, ConstantVal, ConstructorVal, DefinitionVal, InductiveVal, Name, OpaqueVal, QuotVal, RecursorRule as LeanRecRule, RecursorVal, TheoremVal, }; -use crate::ix::ixon::constant::DefKind; -use lean_ffi::nat::Nat; +use ixon::constant::DefKind; -use super::constant::KConst; -use super::env::KEnv; -use super::expr::{ExprData, KExpr, MData}; -use super::id::KId; -use super::level::{KUniv, UnivData}; -use super::mode::Meta; +use ix_kernel::constant::KConst; +use ix_kernel::env::KEnv; +use ix_kernel::expr::{ExprData, KExpr, MData}; +use ix_kernel::id::KId; +use ix_kernel::level::{KUniv, UnivData}; +use ix_kernel::mode::Meta; /// Convert a zero kernel universe to a Lean level. fn egress_level(u: &KUniv, level_params: &[Name]) -> env::Level { @@ -53,7 +53,7 @@ fn egress_levels( } /// Expression egress cache, keyed by content hash. -type Cache = FxHashMap; +type Cache = FxHashMap; /// Convert a zero kernel expression to a Lean expression. fn egress_expr( @@ -343,23 +343,23 @@ use std::sync::Arc; use indexmap::IndexSet; -use crate::ix::address::Address; -use crate::ix::compile::{ +use crate::compile::{ apply_sharing_to_axiom_with_stats, apply_sharing_to_definition_with_stats, apply_sharing_to_mutual_block, apply_sharing_to_quotient_with_stats, apply_sharing_to_recursor_with_stats, }; -use crate::ix::ixon::constant::{ +use ix_common::address::Address; +use ixon::constant::{ Axiom as IxonAxiom, Constant as IxonConstant, ConstantInfo as IxonCI, Constructor as IxonConstructor, ConstructorProj, Definition as IxonDefinition, DefinitionProj, Inductive as IxonInductive, InductiveProj, MutConst as IxonMutConst, Quotient as IxonQuotient, Recursor as IxonRecursor, RecursorProj, RecursorRule as IxonRecursorRule, }; -use crate::ix::ixon::env::{Env as IxonEnv, Named}; -use crate::ix::ixon::expr::Expr as IxonExpr; -use crate::ix::ixon::metadata::ConstantMetaInfo; -use crate::ix::ixon::univ::Univ as IxonUniv; +use ixon::env::{Env as IxonEnv, Named}; +use ixon::expr::Expr as IxonExpr; +use ixon::metadata::ConstantMetaInfo; +use ixon::univ::Univ as IxonUniv; /// Per-constant (or per-block) working context accumulated while converting /// kernel expressions back to Ixon. Mirrors `BlockCache.refs` / `univs` on @@ -1256,14 +1256,17 @@ pub fn ixon_egress( #[cfg(test)] mod tests { use super::*; - use crate::ix::address::Address; - use crate::ix::env::{ + use ix_common::address::Address; + use ix_common::env::{ BinderInfo, DefinitionSafety, ExprData as LeanExprData, Literal, QuotKind, ReducibilityHints, }; - use crate::ix::kernel::constant::RecRule; - use crate::ix::kernel::expr::KExpr; - use crate::ix::kernel::id::KId; + use ix_kernel::constant::{KConst, RecRule}; + use ix_kernel::env::KEnv; + use ix_kernel::expr::KExpr; + use ix_kernel::id::KId; + use ix_kernel::level::KUniv; + use ix_kernel::mode::Meta; fn mk_name(s: &str) -> Name { let mut n = Name::anon(); diff --git a/crates/compile/src/lib.rs b/crates/compile/src/lib.rs new file mode 100644 index 000000000..da38ab964 --- /dev/null +++ b/crates/compile/src/lib.rs @@ -0,0 +1,11 @@ +//! Lean ↔ Ixon compilation pipeline plus kernel ↔ Lean egress. + +pub mod compile; +pub mod condense; +pub mod congruence; +pub mod decompile; +pub mod graph; +pub mod ground; +pub mod kernel_egress; +pub mod mutual; +pub mod store; diff --git a/src/ix/mutual.rs b/crates/compile/src/mutual.rs similarity index 97% rename from src/ix/mutual.rs rename to crates/compile/src/mutual.rs index b3bf81224..b7486a755 100644 --- a/src/ix/mutual.rs +++ b/crates/compile/src/mutual.rs @@ -5,16 +5,15 @@ //! [`ctx_to_all`] / [`all_to_ctx`] functions convert between ordered name //! vectors and index maps. -use lean_ffi::nat::Nat; - -use crate::{ - ix::env::{ - ConstructorVal, DefinitionSafety, DefinitionVal, Expr, InductiveVal, Name, - OpaqueVal, RecursorVal, ReducibilityHints, TheoremVal, - }, - ix::ixon::constant::DefKind, +use bignat::Nat; + +use ix_common::env::{ + ConstructorVal, DefinitionSafety, DefinitionVal, Expr, InductiveVal, Name, + OpaqueVal, RecursorVal, ReducibilityHints, TheoremVal, }; +use ixon::constant::DefKind; + use rustc_hash::FxHashMap; /// A definition-like constant (definition, theorem, or opaque) unified into a @@ -195,7 +194,7 @@ impl MutConst { #[cfg(test)] mod tests { use super::*; - use crate::ix::env::{ConstantVal, Level}; + use ix_common::env::{ConstantVal, Level}; fn n(s: &str) -> Name { Name::str(Name::anon(), s.to_string()) diff --git a/src/ix/store.rs b/crates/compile/src/store.rs similarity index 98% rename from src/ix/store.rs rename to crates/compile/src/store.rs index fe45f508b..33dcaf797 100644 --- a/src/ix/store.rs +++ b/crates/compile/src/store.rs @@ -4,7 +4,7 @@ //! are derived from the first 6 hex characters of the Blake3 hash. This provides //! deterministic addressing: identical content always maps to the same path. -use crate::ix::address::Address; +use ix_common::address::Address; use std::env; use std::fs; use std::io; diff --git a/crates/ffi/Cargo.toml b/crates/ffi/Cargo.toml new file mode 100644 index 000000000..aac4f212b --- /dev/null +++ b/crates/ffi/Cargo.toml @@ -0,0 +1,51 @@ +[package] +name = "ix-ffi" +version.workspace = true +edition.workspace = true +license.workspace = true + +[lib] +crate-type = ["lib", "staticlib"] + +[dependencies] +aiur = { workspace = true } +anyhow = { workspace = true } +bignat = { workspace = true } +blake3 = { workspace = true } +dashmap = { workspace = true, features = ["rayon"] } +indexmap = { workspace = true, features = ["rayon"] } +itertools = { workspace = true } +ix-common = { workspace = true } +ix-compile = { workspace = true } +ixon = { workspace = true } +ix-kernel = { workspace = true } +lean-ffi = { workspace = true } +multi-stark = { workspace = true } +mimalloc = { workspace = true } +num-bigint = { workspace = true } +rayon = { workspace = true } +rustc-hash = { workspace = true } +sha2 = { workspace = true } +tiny-keccak = { workspace = true } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } +tracing-texray = { workspace = true } + +# Iroh dependencies +bytes = { version = "1.10.1", optional = true } +tokio = { version = "1.44.1", optional = true } +iroh = { version = "0.97", optional = true } +iroh-base = { version = "0.97", optional = true } +n0-error = { version = "0.1", optional = true } +getrandom = { version = "0.3", optional = true } +bincode = { version = "2.0.1", optional = true } +serde = { version = "1.0.219", features = ["derive"], optional = true } + +[features] +default = [] +parallel = ["aiur/parallel"] +test-ffi = [] +net = ["bytes", "tokio", "iroh", "iroh-base", "n0-error", "getrandom", "bincode", "serde"] + +[lints] +workspace = true diff --git a/src/ffi/_iroh.rs b/crates/ffi/src/_iroh.rs similarity index 100% rename from src/ffi/_iroh.rs rename to crates/ffi/src/_iroh.rs diff --git a/src/ffi/aiur.rs b/crates/ffi/src/aiur.rs similarity index 95% rename from src/ffi/aiur.rs rename to crates/ffi/src/aiur.rs index aa565fd20..01ad45ace 100644 --- a/src/ffi/aiur.rs +++ b/crates/ffi/src/aiur.rs @@ -3,7 +3,7 @@ use multi_stark::p3_field::integers::QuotientMap; pub mod protocol; pub mod toplevel; -use crate::aiur::G; +use aiur::G; use lean_ffi::object::LeanRef; #[inline] diff --git a/src/ffi/aiur/protocol.rs b/crates/ffi/src/aiur/protocol.rs similarity index 96% rename from src/ffi/aiur/protocol.rs rename to crates/ffi/src/aiur/protocol.rs index 2270ad551..7450a7697 100644 --- a/src/ffi/aiur/protocol.rs +++ b/crates/ffi/src/aiur/protocol.rs @@ -12,18 +12,16 @@ use lean_ffi::object::{ }; use crate::{ - aiur::{ - G, - execute::{IOBuffer, IOKeyInfo}, - synthesis::AiurSystem, - }, - ffi::aiur::{ - lean_unbox_g, lean_unbox_nat_as_usize, toplevel::decode_toplevel, - }, + aiur::{lean_unbox_g, lean_unbox_nat_as_usize, toplevel::decode_toplevel}, lean::{ LeanAiurCommitmentParameters, LeanAiurFriParameters, LeanAiurToplevel, }, }; +use aiur::{ + G, + execute::{IOBuffer, IOKeyInfo}, + synthesis::AiurSystem, +}; // ============================================================================= // External class registration @@ -60,12 +58,12 @@ extern "C" fn rs_aiur_proof_of_bytes( /// `Aiur.AiurSystem.vkBytes : @& AiurSystem → ByteArray` /// /// Serializes the verifying key (`System`) — see -/// `crate::aiur::vk_codec`. +/// `aiur::vk_codec`. #[unsafe(no_mangle)] extern "C" fn rs_aiur_system_vk_bytes( system: LeanExternal>, ) -> LeanByteArray { - let bytes = crate::aiur::vk_codec::aiur_system_to_bytes(system.get()) + let bytes = aiur::vk_codec::aiur_system_to_bytes(system.get()) .expect("VK serialization error"); LeanByteArray::from_bytes(&bytes) } @@ -134,7 +132,7 @@ extern "C" fn rs_aiur_toplevel_execute( let mut query_counts: Vec<(usize, usize)> = Vec::with_capacity( query_record.function_queries.len() + toplevel.memory_sizes.len(), ); - let summarize = |q: &crate::aiur::querymap::QueryMap| -> (usize, usize) { + let summarize = |q: &aiur::querymap::QueryMap| -> (usize, usize) { let mut rows = 0usize; let mut hits = 0usize; for (_, res) in q.iter() { diff --git a/src/ffi/aiur/toplevel.rs b/crates/ffi/src/aiur/toplevel.rs similarity index 97% rename from src/ffi/aiur/toplevel.rs rename to crates/ffi/src/aiur/toplevel.rs index a4f7449b6..bb9cdfdf8 100644 --- a/src/ffi/aiur/toplevel.rs +++ b/crates/ffi/src/aiur/toplevel.rs @@ -4,16 +4,13 @@ use lean_ffi::object::{LeanBorrowed, LeanCtor, LeanRef}; use crate::lean::LeanAiurFunction; -use crate::{ - FxIndexMap, - aiur::{ - G, - bytecode::{Block, Ctrl, Function, FunctionLayout, Op, Toplevel, ValIdx}, - }, - lean::LeanAiurToplevel, +use crate::lean::LeanAiurToplevel; +use aiur::{ + FxIndexMap, G, + bytecode::{Block, Ctrl, Function, FunctionLayout, Op, Toplevel, ValIdx}, }; -use crate::ffi::aiur::{lean_unbox_g, lean_unbox_nat_as_usize}; +use crate::aiur::{lean_unbox_g, lean_unbox_nat_as_usize}; fn decode_vec_val_idx(obj: LeanBorrowed<'_>) -> Vec { obj.as_array().map(|x| lean_unbox_nat_as_usize(&x)) diff --git a/src/ffi/builder.rs b/crates/ffi/src/builder.rs similarity index 100% rename from src/ffi/builder.rs rename to crates/ffi/src/builder.rs diff --git a/src/ffi/byte_array.rs b/crates/ffi/src/byte_array.rs similarity index 100% rename from src/ffi/byte_array.rs rename to crates/ffi/src/byte_array.rs diff --git a/src/ffi/compile.rs b/crates/ffi/src/compile.rs similarity index 97% rename from src/ffi/compile.rs rename to crates/ffi/src/compile.rs index b91e19c3f..cf9285114 100644 --- a/src/ffi/compile.rs +++ b/crates/ffi/src/compile.rs @@ -9,44 +9,44 @@ use std::sync::Arc; -use crate::ix::address::Address; -use crate::ix::compile::{ - CompileOptions, CompileState, compile_env_with_options, -}; -use crate::ix::condense::compute_sccs; -use crate::ix::decompile::decompile_env; -use crate::ix::env::Name; -use crate::ix::graph::build_ref_graph; -use crate::ix::ixon::constant::Constant as IxonConstant; -#[cfg(feature = "test-ffi")] -use crate::ix::ixon::constant::ConstantInfo; -#[cfg(feature = "test-ffi")] -use crate::ix::ixon::expr::Expr as IxonExpr; -use crate::ix::ixon::{Comm, ConstantMeta}; use crate::lean::{ LeanIxBlock, LeanIxCompileError, LeanIxCompilePhases, LeanIxCondensedBlocks, LeanIxConstantInfo, LeanIxDecompileError, LeanIxName, LeanIxRawEnvironment, LeanIxSerializeError, LeanIxonRawBlob, LeanIxonRawComm, LeanIxonRawConst, LeanIxonRawEnv, LeanIxonRawNameEntry, LeanIxonRawNamed, }; -use lean_ffi::nat::Nat; +use ix_common::address::Address; +use ix_common::env::Name; +use ix_compile::compile::{ + CompileOptions, CompileState, compile_env_with_options, +}; +use ix_compile::condense::compute_sccs; +use ix_compile::decompile::decompile_env; +use ix_compile::graph::build_ref_graph; +use ixon::constant::Constant as IxonConstant; +#[cfg(feature = "test-ffi")] +use ixon::constant::ConstantInfo; +#[cfg(feature = "test-ffi")] +use ixon::expr::Expr as IxonExpr; +use ixon::{Comm, ConstantMeta}; use lean_ffi::object::LeanIOResult; +use lean_ffi::object::LeanNat; use lean_ffi::object::{ LeanArray, LeanBorrowed, LeanByteArray, LeanExcept, LeanList, LeanOwned, LeanProd, LeanRef, LeanString, }; -use crate::ffi::builder::LeanBuildCache; -use crate::ffi::ixon::env::decoded_to_ixon_env; -use crate::ffi::lean_env::decode_env; +use crate::builder::LeanBuildCache; use crate::lean::LeanIxAddress; +use crate::lean_env::decode_env; +use crate::lean_ixon::env::decoded_to_ixon_env; #[cfg(feature = "test-ffi")] -use crate::ffi::lean_env::{GlobalCache, decode_name}; +use crate::lean::{LeanIxBlockCompareDetail, LeanIxBlockCompareResult}; #[cfg(feature = "test-ffi")] -use crate::ix::ixon::serialize::put_expr; +use crate::lean_env::{GlobalCache, decode_name}; #[cfg(feature = "test-ffi")] -use crate::lean::{LeanIxBlockCompareDetail, LeanIxBlockCompareResult}; +use ixon::serialize::put_expr; #[cfg(feature = "test-ffi")] use std::collections::HashMap; @@ -1002,17 +1002,17 @@ extern "C" fn rs_get_pre_sharing_exprs( let mut exprs = Vec::new(); for mc in muts { match mc { - crate::ix::ixon::constant::MutConst::Defn(def) => { + ixon::constant::MutConst::Defn(def) => { exprs.push(def.typ.clone()); exprs.push(def.value.clone()); }, - crate::ix::ixon::constant::MutConst::Indc(ind) => { + ixon::constant::MutConst::Indc(ind) => { exprs.push(ind.typ.clone()); for ctor in &ind.ctors { exprs.push(ctor.typ.clone()); } }, - crate::ix::ixon::constant::MutConst::Recr(rec) => { + ixon::constant::MutConst::Recr(rec) => { exprs.push(rec.typ.clone()); for rule in &rec.rules { exprs.push(rule.rhs.clone()); @@ -1091,13 +1091,9 @@ extern "C" fn rs_get_pre_sharing_exprs_len( let mut count = 0; for mc in muts { match mc { - crate::ix::ixon::constant::MutConst::Defn(_) => count += 2, - crate::ix::ixon::constant::MutConst::Indc(ind) => { - count += 1 + ind.ctors.len() - }, - crate::ix::ixon::constant::MutConst::Recr(rec) => { - count += 1 + rec.rules.len() - }, + ixon::constant::MutConst::Defn(_) => count += 2, + ixon::constant::MutConst::Indc(ind) => count += 1 + ind.ctors.len(), + ixon::constant::MutConst::Recr(rec) => count += 1 + rec.rules.len(), } } count @@ -1155,7 +1151,7 @@ extern "C" fn rs_get_compiled_const_count( // Error type FFI builders // ============================================================================= -use crate::ix::ixon::error::{CompileError, DecompileError, SerializeError}; +use ixon::error::{CompileError, DecompileError, SerializeError}; impl LeanIxSerializeError { /// Build a Lean Ixon.SerializeError from a Rust SerializeError. @@ -1244,7 +1240,7 @@ impl LeanIxSerializeError { }, 5 => SerializeError::AddressError, 6 => { - let max = Nat::from_obj(&self.get_obj(0)) + let max = LeanNat::to_nat(&self.get_obj(0)) .to_u64() .and_then(|x| usize::try_from(x).ok()) .unwrap_or(0); @@ -1335,7 +1331,7 @@ impl LeanIxDecompileError { pub fn decode(&self) -> DecompileError { match self.as_ctor().tag() { 0 => { - let refs_len = Nat::from_obj(&self.get_obj(0)) + let refs_len = LeanNat::to_nat(&self.get_obj(0)) .to_u64() .and_then(|x| usize::try_from(x).ok()) .unwrap_or(0); @@ -1344,7 +1340,7 @@ impl LeanIxDecompileError { DecompileError::InvalidRefIndex { idx, refs_len, constant } }, 1 => { - let univs_len = Nat::from_obj(&self.get_obj(0)) + let univs_len = LeanNat::to_nat(&self.get_obj(0)) .to_u64() .and_then(|x| usize::try_from(x).ok()) .unwrap_or(0); @@ -1353,7 +1349,7 @@ impl LeanIxDecompileError { DecompileError::InvalidUnivIndex { idx, univs_len, constant } }, 2 => { - let max = Nat::from_obj(&self.get_obj(0)) + let max = LeanNat::to_nat(&self.get_obj(0)) .to_u64() .and_then(|x| usize::try_from(x).ok()) .unwrap_or(0); @@ -1362,7 +1358,7 @@ impl LeanIxDecompileError { DecompileError::InvalidShareIndex { idx, max, constant } }, 3 => { - let ctx_size = Nat::from_obj(&self.get_obj(0)) + let ctx_size = LeanNat::to_nat(&self.get_obj(0)) .to_u64() .and_then(|x| usize::try_from(x).ok()) .unwrap_or(0); @@ -1371,7 +1367,7 @@ impl LeanIxDecompileError { DecompileError::InvalidRecIndex { idx, ctx_size, constant } }, 4 => { - let max = Nat::from_obj(&self.get_obj(0)) + let max = LeanNat::to_nat(&self.get_obj(0)) .to_u64() .and_then(|x| usize::try_from(x).ok()) .unwrap_or(0); diff --git a/src/ffi/graph.rs b/crates/ffi/src/graph.rs similarity index 94% rename from src/ffi/graph.rs rename to crates/ffi/src/graph.rs index acfcacffa..5dfdd9e8a 100644 --- a/src/ffi/graph.rs +++ b/crates/ffi/src/graph.rs @@ -2,21 +2,21 @@ use std::sync::Arc; -use crate::ix::condense::compute_sccs; -use crate::ix::graph::build_ref_graph; use crate::lean::LeanIxCondensedBlocks; +use ix_compile::condense::compute_sccs; +use ix_compile::graph::build_ref_graph; use lean_ffi::object::{ LeanArray, LeanBorrowed, LeanIOResult, LeanList, LeanOwned, LeanProd, }; -use crate::ffi::builder::LeanBuildCache; -use crate::ffi::lean_env::decode_env; +use crate::builder::LeanBuildCache; use crate::lean::LeanIxName; +use crate::lean_env::decode_env; /// Build an Array (Ix.Name × Array Ix.Name) from a RefMap. pub fn build_ref_graph_array( cache: &mut LeanBuildCache, - refs: &crate::ix::graph::RefMap, + refs: &ix_compile::graph::RefMap, ) -> LeanArray { let arr = LeanArray::alloc(refs.len()); for (i, (name, ref_set)) in refs.iter().enumerate() { @@ -38,7 +38,7 @@ impl LeanIxCondensedBlocks { /// Build a RustCondensedBlocks structure. pub fn build( cache: &mut LeanBuildCache, - condensed: &crate::ix::condense::CondensedBlocks, + condensed: &ix_compile::condense::CondensedBlocks, ) -> Self { // Build lowLinks: Array (Ix.Name × Ix.Name) let low_links_arr = LeanArray::alloc(condensed.low_links.len()); diff --git a/src/iroh.rs b/crates/ffi/src/iroh.rs similarity index 100% rename from src/iroh.rs rename to crates/ffi/src/iroh.rs diff --git a/src/iroh/client.rs b/crates/ffi/src/iroh/client.rs similarity index 100% rename from src/iroh/client.rs rename to crates/ffi/src/iroh/client.rs diff --git a/src/iroh/server.rs b/crates/ffi/src/iroh/server.rs similarity index 100% rename from src/iroh/server.rs rename to crates/ffi/src/iroh/server.rs diff --git a/src/ffi/ix.rs b/crates/ffi/src/ix.rs similarity index 100% rename from src/ffi/ix.rs rename to crates/ffi/src/ix.rs diff --git a/src/ffi/ix/address.rs b/crates/ffi/src/ix/address.rs similarity index 98% rename from src/ffi/ix/address.rs rename to crates/ffi/src/ix/address.rs index f389f8d1d..34512e458 100644 --- a/src/ffi/ix/address.rs +++ b/crates/ffi/src/ix/address.rs @@ -2,8 +2,8 @@ //! //! Address = { hash : ByteArray } - ByteArray wrapper for blake3 Hash -use crate::ix::address::Address; use crate::lean::LeanIxAddress; +use ix_common::address::Address; use lean_ffi::object::{LeanArray, LeanBorrowed, LeanByteArray, LeanOwned}; impl LeanIxAddress { diff --git a/src/ffi/ix/constant.rs b/crates/ffi/src/ix/constant.rs similarity index 91% rename from src/ffi/ix/constant.rs rename to crates/ffi/src/ix/constant.rs index ebc01d8c1..5c3fb70a2 100644 --- a/src/ffi/ix/constant.rs +++ b/crates/ffi/src/ix/constant.rs @@ -10,23 +10,23 @@ //! - Tag 6: ctorInfo (v : ConstructorVal) //! - Tag 7: recInfo (v : RecursorVal) -use crate::ix::env::{ - AxiomVal, ConstantInfo, ConstantVal, ConstructorVal, DefinitionSafety, - DefinitionVal, InductiveVal, Name, OpaqueVal, QuotKind, QuotVal, - RecursorRule, RecursorVal, ReducibilityHints, TheoremVal, -}; use crate::lean::{ LeanIxAxiomVal, LeanIxConstantInfo, LeanIxConstantVal, LeanIxConstructorVal, LeanIxDefinitionVal, LeanIxExpr, LeanIxInductiveVal, LeanIxName, LeanIxOpaqueVal, LeanIxQuotVal, LeanIxRecursorRule, LeanIxRecursorVal, LeanIxReducibilityHints, LeanIxTheoremVal, }; -use lean_ffi::nat::Nat; +use ix_common::env::{ + AxiomVal, ConstantInfo, ConstantVal, ConstructorVal, DefinitionSafety, + DefinitionVal, InductiveVal, Name, OpaqueVal, QuotKind, QuotVal, + RecursorRule, RecursorVal, ReducibilityHints, TheoremVal, +}; #[cfg(feature = "test-ffi")] use lean_ffi::object::LeanBorrowed; +use lean_ffi::object::LeanNat; use lean_ffi::object::{LeanArray, LeanOwned, LeanRef}; -use crate::ffi::builder::LeanBuildCache; +use crate::builder::LeanBuildCache; // ============================================================================= // ConstantVal @@ -121,7 +121,7 @@ impl LeanIxRecursorRule { let ctor = self.as_ctor(); RecursorRule { ctor: LeanIxName(ctor.get(0)).decode(), - n_fields: Nat::from_obj(&ctor.get(1)), + n_fields: LeanNat::to_nat(&ctor.get(1)), rhs: LeanIxExpr(ctor.get(2)).decode(), } } @@ -141,7 +141,7 @@ impl LeanIxRecursorRule { for (i, rule) in rules.iter().enumerate() { // RecursorRule = { ctor : Name, nFields : Nat, rhs : Expr } let ctor_obj = LeanIxName::build(cache, &rule.ctor); - let n_fields_obj = Nat::to_lean(&rule.n_fields); + let n_fields_obj = LeanNat::from_nat(&rule.n_fields); let rhs_obj = LeanIxExpr::build(cache, &rule.rhs); let rule_obj = LeanIxRecursorRule::alloc(0); @@ -253,11 +253,11 @@ impl LeanIxConstantInfo { ConstantInfo::InductInfo(v) => { // InductiveVal = { cnst, numParams, numIndices, all, ctors, numNested, isRec, isUnsafe, isReflexive } let cnst_obj = LeanIxConstantVal::build(cache, &v.cnst); - let num_params_obj = Nat::to_lean(&v.num_params); - let num_indices_obj = Nat::to_lean(&v.num_indices); + let num_params_obj = LeanNat::from_nat(&v.num_params); + let num_indices_obj = LeanNat::from_nat(&v.num_indices); let all_obj = LeanIxName::build_array(cache, &v.all); let ctors_obj = LeanIxName::build_array(cache, &v.ctors); - let num_nested_obj = Nat::to_lean(&v.num_nested); + let num_nested_obj = LeanNat::from_nat(&v.num_nested); // 6 object fields, 3 scalar bytes for bools let induct_val = LeanIxInductiveVal::alloc(0); @@ -280,9 +280,9 @@ impl LeanIxConstantInfo { // ConstructorVal = { cnst, induct, cidx, numParams, numFields, isUnsafe } let cnst_obj = LeanIxConstantVal::build(cache, &v.cnst); let induct_obj = LeanIxName::build(cache, &v.induct); - let cidx_obj = Nat::to_lean(&v.cidx); - let num_params_obj = Nat::to_lean(&v.num_params); - let num_fields_obj = Nat::to_lean(&v.num_fields); + let cidx_obj = LeanNat::from_nat(&v.cidx); + let num_params_obj = LeanNat::from_nat(&v.num_params); + let num_fields_obj = LeanNat::from_nat(&v.num_fields); // 5 object fields, 1 scalar byte for bool let ctor_val = LeanIxConstructorVal::alloc(0); @@ -302,10 +302,10 @@ impl LeanIxConstantInfo { // RecursorVal = { cnst, all, numParams, numIndices, numMotives, numMinors, rules, k, isUnsafe } let cnst_obj = LeanIxConstantVal::build(cache, &v.cnst); let all_obj = LeanIxName::build_array(cache, &v.all); - let num_params_obj = Nat::to_lean(&v.num_params); - let num_indices_obj = Nat::to_lean(&v.num_indices); - let num_motives_obj = Nat::to_lean(&v.num_motives); - let num_minors_obj = Nat::to_lean(&v.num_minors); + let num_params_obj = LeanNat::from_nat(&v.num_params); + let num_indices_obj = LeanNat::from_nat(&v.num_indices); + let num_motives_obj = LeanNat::from_nat(&v.num_motives); + let num_minors_obj = LeanNat::from_nat(&v.num_minors); let rules_obj = LeanIxRecursorRule::build_array(cache, &v.rules); // 7 object fields, 2 scalar bytes for bools @@ -404,11 +404,11 @@ impl LeanIxConstantInfo { ConstantInfo::InductInfo(InductiveVal { cnst: LeanIxConstantVal(inner_val.get_obj(0)).decode(), - num_params: Nat::from_obj(&inner_val.get_obj(1)), - num_indices: Nat::from_obj(&inner_val.get_obj(2)), + num_params: LeanNat::to_nat(&inner_val.get_obj(1)), + num_indices: LeanNat::to_nat(&inner_val.get_obj(2)), all: LeanIxName::decode_array(inner_val.get_obj(3).as_array()), ctors: LeanIxName::decode_array(inner_val.get_obj(4).as_array()), - num_nested: Nat::from_obj(&inner_val.get_obj(5)), + num_nested: LeanNat::to_nat(&inner_val.get_obj(5)), is_rec, is_unsafe, is_reflexive, @@ -421,9 +421,9 @@ impl LeanIxConstantInfo { ConstantInfo::CtorInfo(ConstructorVal { cnst: LeanIxConstantVal(inner_val.get_obj(0)).decode(), induct: LeanIxName(inner_val.get_obj(1)).decode(), - cidx: Nat::from_obj(&inner_val.get_obj(2)), - num_params: Nat::from_obj(&inner_val.get_obj(3)), - num_fields: Nat::from_obj(&inner_val.get_obj(4)), + cidx: LeanNat::to_nat(&inner_val.get_obj(2)), + num_params: LeanNat::to_nat(&inner_val.get_obj(3)), + num_fields: LeanNat::to_nat(&inner_val.get_obj(4)), is_unsafe, }) }, @@ -440,10 +440,10 @@ impl LeanIxConstantInfo { ConstantInfo::RecInfo(RecursorVal { cnst: LeanIxConstantVal(inner_val.get_obj(0)).decode(), all: LeanIxName::decode_array(inner_val.get_obj(1).as_array()), - num_params: Nat::from_obj(&inner_val.get_obj(2)), - num_indices: Nat::from_obj(&inner_val.get_obj(3)), - num_motives: Nat::from_obj(&inner_val.get_obj(4)), - num_minors: Nat::from_obj(&inner_val.get_obj(5)), + num_params: LeanNat::to_nat(&inner_val.get_obj(2)), + num_indices: LeanNat::to_nat(&inner_val.get_obj(3)), + num_motives: LeanNat::to_nat(&inner_val.get_obj(4)), + num_minors: LeanNat::to_nat(&inner_val.get_obj(5)), rules, k, is_unsafe, diff --git a/src/ffi/ix/data.rs b/crates/ffi/src/ix/data.rs similarity index 92% rename from src/ffi/ix/data.rs rename to crates/ffi/src/ix/data.rs index 7fbb62280..355be8bbb 100644 --- a/src/ffi/ix/data.rs +++ b/crates/ffi/src/ix/data.rs @@ -1,18 +1,18 @@ //! Ix.DataValue, Ix.Syntax, Ix.SourceInfo build/decode/roundtrip FFI. -use crate::ix::env::{ - DataValue, Int, Name, SourceInfo, Substring, Syntax, SyntaxPreresolved, -}; use crate::lean::{ LeanIxDataValue, LeanIxInt, LeanIxName, LeanIxSourceInfo, LeanIxSubstring, LeanIxSyntax, LeanIxSyntaxPreresolved, }; -use lean_ffi::nat::Nat; +use ix_common::env::{ + DataValue, Int, Name, SourceInfo, Substring, Syntax, SyntaxPreresolved, +}; #[cfg(feature = "test-ffi")] use lean_ffi::object::LeanBorrowed; +use lean_ffi::object::LeanNat; use lean_ffi::object::{LeanArray, LeanOwned, LeanProd, LeanRef, LeanString}; -use crate::ffi::builder::LeanBuildCache; +use crate::builder::LeanBuildCache; impl LeanIxInt { /// Build a Ix.Int (ofNat or negSucc). @@ -20,12 +20,12 @@ impl LeanIxInt { match int { Int::OfNat(n) => { let ctor = LeanIxInt::alloc(0); - ctor.set_obj(0, Nat::to_lean(n)); + ctor.set_obj(0, LeanNat::from_nat(n)); ctor }, Int::NegSucc(n) => { let ctor = LeanIxInt::alloc(1); - ctor.set_obj(0, Nat::to_lean(n)); + ctor.set_obj(0, LeanNat::from_nat(n)); ctor }, } @@ -37,7 +37,7 @@ impl LeanIxInt { /// Ix.Int: ofNat (tag 0, 1 field) | negSucc (tag 1, 1 field) pub fn decode(&self) -> Int { let ctor = self.as_ctor(); - let nat = Nat::from_obj(&ctor.get(0)); + let nat = LeanNat::to_nat(&ctor.get(0)); match ctor.tag() { 0 => Int::OfNat(nat), 1 => Int::NegSucc(nat), @@ -51,8 +51,8 @@ impl LeanIxSubstring { pub fn build(ss: &Substring) -> Self { let ctor = LeanIxSubstring::alloc(0); ctor.set_obj(0, LeanString::new(ss.str.as_str())); - ctor.set_obj(1, Nat::to_lean(&ss.start_pos)); - ctor.set_obj(2, Nat::to_lean(&ss.stop_pos)); + ctor.set_obj(1, LeanNat::from_nat(&ss.start_pos)); + ctor.set_obj(2, LeanNat::from_nat(&ss.stop_pos)); ctor } } @@ -63,8 +63,8 @@ impl LeanIxSubstring { let ctor = self.as_ctor(); Substring { str: ctor.get(0).as_string().to_string(), - start_pos: Nat::from_obj(&ctor.get(1)), - stop_pos: Nat::from_obj(&ctor.get(2)), + start_pos: LeanNat::to_nat(&ctor.get(1)), + stop_pos: LeanNat::to_nat(&ctor.get(2)), } } } @@ -77,16 +77,16 @@ impl LeanIxSourceInfo { SourceInfo::Original(leading, pos, trailing, end_pos) => { let ctor = LeanIxSourceInfo::alloc(0); ctor.set_obj(0, LeanIxSubstring::build(leading)); - ctor.set_obj(1, Nat::to_lean(pos)); + ctor.set_obj(1, LeanNat::from_nat(pos)); ctor.set_obj(2, LeanIxSubstring::build(trailing)); - ctor.set_obj(3, Nat::to_lean(end_pos)); + ctor.set_obj(3, LeanNat::from_nat(end_pos)); ctor }, // | synthetic (pos : Nat) (endPos : Nat) (canonical : Bool) -- tag 1 SourceInfo::Synthetic(pos, end_pos, canonical) => { let ctor = LeanIxSourceInfo::alloc(1); - ctor.set_obj(0, Nat::to_lean(pos)); - ctor.set_obj(1, Nat::to_lean(end_pos)); + ctor.set_obj(0, LeanNat::from_nat(pos)); + ctor.set_obj(1, LeanNat::from_nat(end_pos)); ctor.set_num_8(0, u8::from(*canonical)); ctor }, @@ -108,9 +108,9 @@ impl LeanIxSourceInfo { // original SourceInfo::Original( LeanIxSubstring(ctor.get(0)).decode(), - Nat::from_obj(&ctor.get(1)), + LeanNat::to_nat(&ctor.get(1)), LeanIxSubstring(ctor.get(2)).decode(), - Nat::from_obj(&ctor.get(3)), + LeanNat::to_nat(&ctor.get(3)), ) }, 1 => { @@ -118,8 +118,8 @@ impl LeanIxSourceInfo { let canonical = self.get_num_8(0) != 0; SourceInfo::Synthetic( - Nat::from_obj(&self.get_obj(0)), - Nat::from_obj(&self.get_obj(1)), + LeanNat::to_nat(&self.get_obj(0)), + LeanNat::to_nat(&self.get_obj(1)), canonical, ) }, @@ -298,7 +298,7 @@ impl LeanIxDataValue { }, DataValue::OfNat(n) => { let ctor = LeanIxDataValue::alloc(3); - ctor.set_obj(0, Nat::to_lean(n)); + ctor.set_obj(0, LeanNat::from_nat(n)); ctor }, DataValue::OfInt(i) => { @@ -351,13 +351,13 @@ impl LeanIxDataValue { }, 3 => { // ofNat: 1 object field - DataValue::OfNat(Nat::from_obj(&ctor.get(0))) + DataValue::OfNat(LeanNat::to_nat(&ctor.get(0))) }, 4 => { // ofInt: 1 object field let inner = ctor.get(0); let inner_ctor = inner.as_ctor(); - let nat = Nat::from_obj(&inner_ctor.get(0)); + let nat = LeanNat::to_nat(&inner_ctor.get(0)); match inner_ctor.tag() { 0 => DataValue::OfInt(Int::OfNat(nat)), 1 => DataValue::OfInt(Int::NegSucc(nat)), diff --git a/src/ffi/ix/env.rs b/crates/ffi/src/ix/env.rs similarity index 97% rename from src/ffi/ix/env.rs rename to crates/ffi/src/ix/env.rs index c4179fe28..3800bc614 100644 --- a/src/ffi/ix/env.rs +++ b/crates/ffi/src/ix/env.rs @@ -2,15 +2,15 @@ use rustc_hash::FxHashMap; -use crate::ix::env::{ConstantInfo, Name}; use crate::lean::{ LeanIxConstantInfo, LeanIxEnvironment, LeanIxName, LeanIxRawEnvironment, }; +use ix_common::env::{ConstantInfo, Name}; use lean_ffi::object::{ LeanArray, LeanBorrowed, LeanCtor, LeanOwned, LeanProd, LeanRef, }; -use crate::ffi::builder::LeanBuildCache; +use crate::builder::LeanBuildCache; // ============================================================================= // HashMap Building @@ -140,7 +140,7 @@ impl LeanIxRawEnvironment { /// so we return just the array, not a structure containing it. pub fn build( cache: &mut LeanBuildCache, - consts: &crate::ix::env::Env, + consts: &ix_common::env::Env, ) -> Self { // Build consts array: Array (Name × ConstantInfo) let consts_arr = LeanArray::alloc(consts.len()); @@ -242,7 +242,7 @@ pub extern "C" fn rs_roundtrip_ix_environment( env_ptr: LeanIxEnvironment>, ) -> LeanIxRawEnvironment { let decoded = env_ptr.decode(); - let env: crate::ix::env::Env = decoded.into_iter().collect(); + let env: ix_common::env::Env = decoded.into_iter().collect(); let mut cache = LeanBuildCache::with_capacity(env.len()); LeanIxRawEnvironment::build(&mut cache, &env) } diff --git a/src/ffi/ix/expr.rs b/crates/ffi/src/ix/expr.rs similarity index 96% rename from src/ffi/ix/expr.rs rename to crates/ffi/src/ix/expr.rs index ced0bb755..4b5e4bfc1 100644 --- a/src/ffi/ix/expr.rs +++ b/crates/ffi/src/ix/expr.rs @@ -14,18 +14,18 @@ //! - Tag 10: mdata (data : Array (Name × DataValue)) (expr : Expr) (hash : Address) //! - Tag 11: proj (typeName : Name) (idx : Nat) (struct : Expr) (hash : Address) -use crate::ffi::builder::LeanBuildCache; -use crate::ix::env::{ - BinderInfo, DataValue, Expr, ExprData, Level, Literal, Name, -}; +use crate::builder::LeanBuildCache; use crate::lean::LeanIxAddress; use crate::lean::{ LeanIxBinderInfo, LeanIxDataValue, LeanIxExpr, LeanIxLevel, LeanIxLiteral, LeanIxName, }; -use lean_ffi::nat::Nat; +use ix_common::env::{ + BinderInfo, DataValue, Expr, ExprData, Level, Literal, Name, +}; #[cfg(feature = "test-ffi")] use lean_ffi::object::LeanBorrowed; +use lean_ffi::object::LeanNat; use lean_ffi::object::{LeanOwned, LeanRef, LeanString}; impl LeanIxExpr { @@ -40,7 +40,7 @@ impl LeanIxExpr { let result = match expr.as_data() { ExprData::Bvar(idx, h) => { let ctor = LeanIxExpr::alloc(0); - ctor.set_obj(0, Nat::to_lean(idx)); + ctor.set_obj(0, LeanNat::from_nat(idx)); ctor.set_obj(1, LeanIxAddress::build_from_hash(h)); ctor }, @@ -139,7 +139,7 @@ impl LeanIxExpr { }, ExprData::Proj(type_name, idx, struct_expr, h) => { let name_obj = LeanIxName::build(cache, type_name); - let idx_obj = Nat::to_lean(idx); + let idx_obj = LeanNat::from_nat(idx); let struct_obj = Self::build(cache, struct_expr); let ctor = LeanIxExpr::alloc(11); ctor.set_obj(0, name_obj); @@ -162,7 +162,7 @@ impl LeanIxExpr { match ctor.tag() { 0 => { // bvar - let idx = Nat::from_obj(&ctor.get(0)); + let idx = LeanNat::to_nat(&ctor.get(0)); Expr::bvar(idx) }, 1 => { @@ -247,7 +247,7 @@ impl LeanIxExpr { 11 => { // proj: typeName, idx, struct, hash let type_name = LeanIxName(ctor.get(0)).decode(); - let idx = Nat::from_obj(&ctor.get(1)); + let idx = LeanNat::to_nat(&ctor.get(1)); let struct_expr = LeanIxExpr(ctor.get(2)).decode(); Expr::proj(type_name, idx, struct_expr) @@ -263,7 +263,7 @@ impl LeanIxLiteral { match lit { Literal::NatVal(n) => { let ctor = LeanIxLiteral::alloc(0); - ctor.set_obj(0, Nat::to_lean(n)); + ctor.set_obj(0, LeanNat::from_nat(n)); ctor }, Literal::StrVal(s) => { @@ -282,7 +282,7 @@ impl LeanIxLiteral { match ctor.tag() { 0 => { // natVal - let nat = Nat::from_obj(&ctor.get(0)); + let nat = LeanNat::to_nat(&ctor.get(0)); Literal::NatVal(nat) }, 1 => { diff --git a/src/ffi/ix/level.rs b/crates/ffi/src/ix/level.rs similarity index 98% rename from src/ffi/ix/level.rs rename to crates/ffi/src/ix/level.rs index 5039c386a..fead881f1 100644 --- a/src/ffi/ix/level.rs +++ b/crates/ffi/src/ix/level.rs @@ -8,11 +8,11 @@ //! - Tag 4: param (n : Name) (hash : Address) //! - Tag 5: mvar (n : Name) (hash : Address) -use crate::ix::env::{Level, LevelData}; use crate::lean::{LeanIxLevel, LeanIxName}; +use ix_common::env::{Level, LevelData}; use lean_ffi::object::{LeanArray, LeanBorrowed, LeanOwned, LeanRef}; -use crate::ffi::builder::LeanBuildCache; +use crate::builder::LeanBuildCache; use crate::lean::LeanIxAddress; impl LeanIxLevel { diff --git a/src/ffi/ix/name.rs b/crates/ffi/src/ix/name.rs similarity index 94% rename from src/ffi/ix/name.rs rename to crates/ffi/src/ix/name.rs index 62e72ec0b..be3597239 100644 --- a/src/ffi/ix/name.rs +++ b/crates/ffi/src/ix/name.rs @@ -5,14 +5,14 @@ //! - Tag 1: str (parent : Name) (s : String) (hash : Address) //! - Tag 2: num (parent : Name) (i : Nat) (hash : Address) -use crate::ix::env::{Name, NameData}; use crate::lean::LeanIxName; -use lean_ffi::nat::Nat; +use ix_common::env::{Name, NameData}; +use lean_ffi::object::LeanNat; use lean_ffi::object::{ LeanArray, LeanBorrowed, LeanOwned, LeanRef, LeanString, }; -use crate::ffi::builder::LeanBuildCache; +use crate::builder::LeanBuildCache; use crate::lean::LeanIxAddress; impl LeanIxName { @@ -41,7 +41,7 @@ impl LeanIxName { }, NameData::Num(parent, n, h) => { let parent_obj = Self::build(cache, parent); - let n_obj = Nat::to_lean(n); + let n_obj = LeanNat::from_nat(n); let ctor = LeanIxName::alloc(2); ctor.set_obj(0, parent_obj); ctor.set_obj(1, n_obj); @@ -85,7 +85,7 @@ impl LeanIxName { 2 => { // num: parent, i, hash let parent = LeanIxName(ctor.get(0)).decode(); - let i = Nat::from_obj(&ctor.get(1)); + let i = LeanNat::to_nat(&ctor.get(1)); Name::num(parent, i) }, _ => panic!("Invalid Ix.Name tag: {}", ctor.tag()), diff --git a/src/ffi/keccak.rs b/crates/ffi/src/keccak.rs similarity index 100% rename from src/ffi/keccak.rs rename to crates/ffi/src/keccak.rs diff --git a/src/ffi/kernel.rs b/crates/ffi/src/kernel.rs similarity index 93% rename from src/ffi/kernel.rs rename to crates/ffi/src/kernel.rs index 84ba7d844..a386d6a2f 100644 --- a/src/ffi/kernel.rs +++ b/crates/ffi/src/kernel.rs @@ -37,7 +37,7 @@ use std::thread; use std::time::{Duration, Instant}; use lean_ffi::include::lean_object; -use lean_ffi::nat::Nat; +use lean_ffi::object::LeanNat; use rustc_hash::FxHashMap; use lean_ffi::object::{ @@ -48,40 +48,36 @@ use lean_ffi::object::{ use crate::lean::LeanIxCheckError; #[cfg(feature = "test-ffi")] -use crate::ffi::lean_env::{GlobalCache, decode_name}; -use crate::ffi::lean_env::{decode_env, decode_name_array}; -use crate::ix::address::Address; -use crate::ix::compile::{ +use crate::lean_env::{GlobalCache, decode_name}; +use crate::lean_env::{decode_env, decode_name_array}; +use ix_common::address::Address; +use ix_common::env::{Name, NameData}; +use ix_compile::compile::{ CompileOptions, CompileState, compile_env_with_options, }; #[cfg(feature = "test-ffi")] -use crate::ix::decompile::decompile_env; -use crate::ix::env::{Name, NameData}; -use crate::ix::ixon::constant::ConstantInfo as IxonCI; +use ix_compile::decompile::decompile_env; #[cfg(feature = "test-ffi")] -use crate::ix::ixon::constant::MutConst as IxonMutConst; -use crate::ix::ixon::env::Env as IxonEnv; -#[cfg(feature = "test-ffi")] -use crate::ix::ixon::expr::Expr as IxonExpr; -use crate::ix::ixon::metadata::ConstantMetaInfo; -#[cfg(feature = "test-ffi")] -use crate::ix::kernel::egress::{ixon_egress, lean_egress}; -use crate::ix::kernel::env::KEnv; -use crate::ix::kernel::error::TcError; -use crate::ix::kernel::id::KId; -use crate::ix::kernel::ingress::{ +use ix_compile::kernel_egress::{ixon_egress, lean_egress}; +use ix_kernel::env::KEnv; +use ix_kernel::error::TcError; +use ix_kernel::id::KId; +use ix_kernel::ingress::{ IxonIngressLookups, build_ixon_ingress_lookups, ingress_const_shallow_into_kenv_with_lookups, ixon_ingress_owned, }; -use crate::ix::kernel::ingress::{ - anon_ctor_proj_addr, anon_defn_proj_addr, anon_indc_proj_addr, - anon_recr_proj_addr, -}; #[cfg(feature = "test-ffi")] -use crate::ix::kernel::ingress::{ixon_ingress, lean_ingress}; -use crate::ix::kernel::mode::{Anon, CheckDupLevelParams, KernelMode, Meta}; -use crate::ix::kernel::tc::TypeChecker; -use crate::ix::profile::{BlockProfile, ProfileBuilder, ProfileSink}; +use ix_kernel::ingress::{ixon_ingress, lean_ingress}; +use ix_kernel::mode::{Anon, CheckDupLevelParams, KernelMode, Meta}; +use ix_kernel::profile::{BlockProfile, ProfileBuilder, ProfileSink}; +use ix_kernel::tc::TypeChecker; +use ixon::constant::ConstantInfo as IxonCI; +#[cfg(feature = "test-ffi")] +use ixon::constant::MutConst as IxonMutConst; +use ixon::env::Env as IxonEnv; +#[cfg(feature = "test-ffi")] +use ixon::expr::Expr as IxonExpr; +use ixon::metadata::ConstantMetaInfo; unsafe extern "C" { fn lean_name_mk_string( @@ -448,8 +444,7 @@ fn poison_second_rec_rule_returns_first_minor( let rec_arc = ixon_env.get_const(&rec_addr).ok_or_else(|| { format!("{}: missing constant {}", rec_name.pretty(), rec_addr.hex()) })?; - let mut rec_constant: crate::ix::ixon::constant::Constant = - (*rec_arc).clone(); + let mut rec_constant: ixon::constant::Constant = (*rec_arc).clone(); drop(rec_arc); match &mut rec_constant.info { @@ -485,8 +480,7 @@ fn poison_second_rec_rule_returns_first_minor( block_addr.hex() ) })?; - let mut block_constant: crate::ix::ixon::constant::Constant = - (*block_arc).clone(); + let mut block_constant: ixon::constant::Constant = (*block_arc).clone(); drop(block_arc); match &mut block_constant.info { IxonCI::Muts(members) => { @@ -533,7 +527,7 @@ fn poison_second_rec_rule_returns_first_minor( #[cfg(feature = "test-ffi")] fn poison_recursor_rule_payload( - rec: &mut crate::ix::ixon::constant::Recursor, + rec: &mut ixon::constant::Recursor, ) -> Result<(), String> { if rec.rules.len() < 2 { return Err(format!( @@ -746,7 +740,7 @@ pub extern "C" fn rs_kernel_ixon_names( /// to downstream consumers (Aiur, kernel primitive resolution). #[unsafe(no_mangle)] pub extern "C" fn rs_prim_addrs_canonical() -> LeanIOResult { - let table = crate::ix::kernel::primitive::PrimAddrs::lean_parity_table(); + let table = ix_kernel::primitive::PrimAddrs::lean_parity_table(); let arr = LeanArray::alloc(table.len()); for (i, (name, hex)) in table.iter().enumerate() { let name_obj: LeanOwned = LeanString::new(name).into(); @@ -792,7 +786,7 @@ fn build_lean_name(name: &Name) -> LeanOwned { }, NameData::Num(parent, n, _) => { let parent = build_lean_name(parent); - let part = Nat::to_lean(n); + let part = LeanNat::from_nat(n); unsafe { LeanOwned::from_raw(lean_name_mk_numeral( parent.into_raw(), @@ -1298,6 +1292,11 @@ fn resolve_kernel_check_workers_from( // The lazy ingress mechanism (in `tc.rs`) handles cross-block faults // without consulting metadata. +/// FFI-side anon work item with per-target result-slot indices. +/// +/// Wraps [`ix_kernel::anon_work::AnonWorkItem`] with the index +/// mapping the parallel runner needs to write per-target results +/// into a flat `OnceLock` vector for the FFI return ABI. #[derive(Clone, Debug)] enum AnonWorkItem { /// A standalone (non-mutual, non-projection) constant. @@ -1314,93 +1313,34 @@ enum AnonWorkItem { /// target addresses (one per result slot). Skips projection constants /// (covered by their parent block) and Muts addresses themselves /// (blocks aren't kernel KIds). +/// +/// Delegates the enumeration to +/// [`ix_kernel::anon_work::build_anon_work`] (shared with the +/// SP1/Zisk guests) and layers the FFI's per-target result-slot +/// bookkeeping on top. fn build_anon_work( env: &IxonEnv, ) -> Result<(Vec, Vec
), String> { - use crate::ix::ixon::constant::ConstantInfo as CI; - use crate::ix::ixon::constant::MutConst as MC; - use crate::ix::ixon::lazy::ConstVariantTag as Tag; + use ix_kernel::anon_work::AnonWorkItem as KItem; - let mut work: Vec = Vec::new(); + let kernel_work = ix_kernel::anon_work::build_anon_work(env)?; + let mut work: Vec = Vec::with_capacity(kernel_work.len()); let mut addrs: Vec
= Vec::new(); - - // Sort keys for deterministic ordering across runs. - let mut keys: Vec
= - env.consts.iter().map(|e| e.key().clone()).collect(); - keys.sort_unstable(); - - // Dispatch on the outer Tag4 byte via `peek_variant` — no body - // parse, no allocation. Only `Muts` blocks require a full - // materialization (to enumerate members for projection-address - // computation); the resulting `Arc` drops at the end of - // that match arm. Standalones (Defn/Recr/Axio/Quot, ~95% of the - // env) and projection skips don't even touch the body. - // - // Before this change, every constant was fully materialized here - // and (worse) pinned forever in `LazyConstant.cache`'s OnceLock. - // For mathlib that pinned ~30 GB of parsed `Arc` trees in - // the shared `Arc` before kernel checking even started. - // The cache-free `LazyConstant` policy + this peek path keep - // env-side memory bounded to "bytes (mmap'd) + per-const headers". - for addr in keys { - let lc = env.consts.get(&addr).ok_or_else(|| { - format!("build_anon_work: missing const at {}", addr.hex()) - })?; - let tag = lc.value().peek_variant().map_err(|e| { - format!("build_anon_work: peek_variant {}: {e}", addr.hex()) - })?; - match tag { - Tag::IPrj | Tag::CPrj | Tag::RPrj | Tag::DPrj => { - // Skip; covered by parent block. - }, - Tag::Defn | Tag::Recr | Tag::Axio | Tag::Quot => { + for item in kernel_work { + match item { + KItem::Standalone { addr } => { let result_idx = addrs.len(); addrs.push(addr.clone()); - work.push(AnonWorkItem::Standalone { result_idx, addr: addr.clone() }); + work.push(AnonWorkItem::Standalone { result_idx, addr }); }, - Tag::Muts => { - // Materialize once to enumerate members; the `Arc` - // drops at the end of this arm — no cache retention. - let constant = lc.value().get().map_err(|e| { - format!("build_anon_work: materialize Muts {}: {e}", addr.hex()) - })?; - let CI::Muts(members) = &constant.info else { - return Err(format!( - "build_anon_work: Tag::Muts but ConstantInfo is {:?} at {}", - constant.info.variant(), - addr.hex() - )); - }; - // Compute kernel-checkable targets deterministically. Each - // member contributes its projection address; inductive members - // contribute one CPrj per constructor. - let mut targets: Vec
= Vec::new(); - for (i, member) in members.iter().enumerate() { - let i = i as u64; - let member_addr = match member { - MC::Defn(_) => anon_defn_proj_addr(&addr, i), - MC::Indc(_) => anon_indc_proj_addr(&addr, i), - MC::Recr(_) => anon_recr_proj_addr(&addr, i), - }; - targets.push(member_addr); - if let MC::Indc(ind) = member { - for cidx in 0..ind.ctors.len() as u64 { - targets.push(anon_ctor_proj_addr(&addr, i, cidx)); - } - } - } - if targets.is_empty() { - continue; - } - let primary_addr = targets[0].clone(); - let result_idxs: Vec = - (addrs.len()..addrs.len() + targets.len()).collect(); + KItem::Block { primary, targets, .. } => { + let start = addrs.len(); + let result_idxs: Vec = (start..start + targets.len()).collect(); addrs.extend(targets); - work.push(AnonWorkItem::Block { primary_addr, result_idxs }); + work.push(AnonWorkItem::Block { primary_addr: primary, result_idxs }); }, } } - Ok((work, addrs)) } @@ -1685,7 +1625,7 @@ pub extern "C" fn rs_kernel_check_anon( // =========================================================================== // Sharding profiler: run the anon kernel out of circuit over a `.ixe`, -// recording per-block heartbeats + the delta-unfold graph into a `.ixesp`. +// recording per-block heartbeats + the delta-unfold graph into a `.ixprof`. // See `plans/sharding.md`. // =========================================================================== @@ -1722,6 +1662,61 @@ fn profile_block_size(env: &IxonEnv, block: &Address) -> u32 { .map_or(0, |b| b.len().min(u32::MAX as usize) as u32) } +/// Print the general-purpose cost breakdown for `ix profile` — the kernel-work +/// metrics plus the predicted Zisk leaf cost/RAM (à la `cargo-zisk … -p summary`). +// `steps as f64` is a display-only cast for `{:.2e}` formatting; precision loss +// past 2⁵³ steps is irrelevant to a two-sig-fig estimate. +#[allow(clippy::cast_precision_loss)] +fn print_profile_summary( + env_path: &str, + sink: &ProfileSink, + profile: &BlockProfile, +) { + use ix_kernel::shard::{ + SHARD_STEP_FLOOR, STEPS_PER_HEARTBEAT, STEPS_PER_INGRESS_BYTE, + STEPS_PER_SUBST, block_step_cost, ram_gib_for_steps, + }; + let (mut hb, mut subst, mut whnf, mut defeq, mut nat) = + (0u64, 0u64, 0u64, 0u64, 0u64); + for rec in sink.records.values() { + hb = hb.saturating_add(rec.fuel); + subst = subst.saturating_add(rec.ops.subst_nodes); + whnf = whnf.saturating_add(rec.ops.whnf_calls); + defeq = defeq.saturating_add(rec.ops.def_eq_calls); + nat = nat.saturating_add(rec.ops.nat_arith); + } + let ingress: u64 = + profile.blocks().iter().map(|b| u64::from(b.serialized_size)).sum(); + let steps: u64 = profile + .blocks() + .iter() + .map(block_step_cost) + .fold(SHARD_STEP_FLOOR, u64::saturating_add); + let ram_gib = ram_gib_for_steps(steps); + let warn = if ram_gib > 250.0 { + " → exceeds a 250 GiB box; shard it (ix shard --max-ram G)" + } else { + "" + }; + eprintln!( + "\n── ix profile: {env_path} ──\n\ + constants {} blocks {}\n\n\ + kernel work\n\ + \u{20}\u{20}heartbeats {hb:>14}\n\ + \u{20}\u{20}subst nodes {subst:>14}\n\ + \u{20}\u{20}whnf calls {whnf:>14}\n\ + \u{20}\u{20}def-eq calls {defeq:>14}\n\ + \u{20}\u{20}nat-arith {nat:>14}\n\ + \u{20}\u{20}ingress bytes {ingress:>14}\n\n\ + predicted Zisk leaf ({SHARD_STEP_FLOOR} + {STEPS_PER_HEARTBEAT}·hb + {STEPS_PER_SUBST}·subst + {STEPS_PER_INGRESS_BYTE}·bytes)\n\ + \u{20}\u{20}cycles ≈ {:.2e}\n\ + \u{20}\u{20}RAM ≈ {ram_gib:.0} GiB{warn}", + sink.records.len(), + profile.num_blocks(), + steps as f64, + ); +} + /// Aggregate per-constant records into a block-level [`BlockProfile`]: map each /// consumer/producer constant to its home block, attach serialized sizes, and /// accumulate heartbeats + delta edges. @@ -1741,10 +1736,10 @@ fn build_block_profile(env: &IxonEnv, merged: &ProfileSink) -> BlockProfile { }; for (consumer, rec) in &merged.records { let (cblock, csize) = resolve(consumer); - builder.block(cblock.clone(), rec.fuel, csize, 1); + builder.block(cblock.clone(), rec.fuel, csize, 1, rec.ops.subst_nodes); for prod in &rec.producers { let (pblock, psize) = resolve(prod); - builder.block(pblock.clone(), 0, psize, 0); + builder.block(pblock.clone(), 0, psize, 0, 0); builder.delta_edge(cblock.clone(), pblock); } } @@ -1851,7 +1846,7 @@ fn run_anon_profile_parallel( Ok((passed.load(Ordering::Relaxed), failed.load(Ordering::Relaxed), merged)) } -/// Profile a `.ixe` out of circuit and write the `.ixesp` sidecar. Pure-Rust +/// Profile a `.ixe` out of circuit and write the `.ixprof` sidecar. Pure-Rust /// entry point (used by the FFI wrapper and Rust tests). pub fn profile_anon_ixe( path: &str, @@ -1880,6 +1875,7 @@ pub fn profile_anon_ixe( run_start.elapsed() ); let profile = build_block_profile(&env_arc, &merged); + print_profile_summary(path, &merged, &profile); let bytes = profile.to_bytes(); std::fs::write(out, &bytes).map_err(|e| format!("write {out}: {e}"))?; eprintln!( @@ -1898,7 +1894,7 @@ pub fn profile_anon_ixe( }) } -/// FFI: profile a `.ixe` out of circuit and write a `.ixesp` sidecar. +/// FFI: profile a `.ixe` out of circuit and write a `.ixprof` sidecar. #[unsafe(no_mangle)] pub extern "C" fn rs_kernel_profile_anon( env_path: LeanString>, @@ -1925,7 +1921,7 @@ pub extern "C" fn rs_kernel_profile_anon( } } -/// FFI: partition a `.ixesp` into `num_shards` shards and write a `.ixes` +/// FFI: partition a `.ixprof` into `num_shards` shards and write a `.ixes` /// manifest. Prints a what-if report to stderr. #[allow(clippy::cast_precision_loss)] // balance_pct is a small percentage #[unsafe(no_mangle)] @@ -1933,17 +1929,21 @@ pub extern "C" fn rs_shard_esp( esp_path: LeanString>, num_shards: LeanString>, balance_pct: LeanString>, + parallelism: LeanString>, out_path: LeanString>, ) -> LeanIOResult { let num_shards = num_shards.to_string().parse::().unwrap_or(1); let balance_pct = balance_pct.to_string().parse::().unwrap_or(5); + let parallelism = + parallelism.to_string().parse::().unwrap_or(1).max(1); let out = out_path.to_string(); let out_opt = if out.is_empty() { None } else { Some(out.as_str()) }; let balance = (balance_pct as f64) / 100.0; - match crate::ix::shard::shard_esp( + match ix_kernel::shard::shard_esp( &esp_path.to_string(), num_shards, balance, + parallelism, out_opt, ) { Ok(report) => { @@ -1954,6 +1954,74 @@ pub extern "C" fn rs_shard_esp( } } +/// Total system RAM in GiB from `/proc/meminfo` (Linux); `None` if unreadable. +fn system_ram_gib() -> Option { + let s = std::fs::read_to_string("/proc/meminfo").ok()?; + let rest = s.lines().find_map(|l| l.strip_prefix("MemTotal:"))?; + let kib: f64 = rest.trim().trim_end_matches("kB").trim().parse().ok()?; + Some(kib / 1024.0 / 1024.0) // KiB → GiB +} + +/// FFI: partition a `.ixprof` to a per-shard cycle/RAM budget and write a +/// `.ixes` manifest. `max_cycles` is a guest-STEP cap; if `ram_gb` > 0 it is +/// converted via the measured prover RAM model and overrides `max_cycles`. Pass +/// "0" for both to default the budget to detected system RAM. +#[allow(clippy::cast_precision_loss)] +#[unsafe(no_mangle)] +pub extern "C" fn rs_shard_esp_cap( + esp_path: LeanString>, + max_cycles: LeanString>, + ram_gb: LeanString>, + balance_pct: LeanString>, + parallelism: LeanString>, + out_path: LeanString>, +) -> LeanIOResult { + let mc = max_cycles.to_string().parse::().unwrap_or(0); + let mut ram = ram_gb.to_string().parse::().unwrap_or(0.0); + let parallelism = + parallelism.to_string().parse::().unwrap_or(1).max(1); + let balance = + (balance_pct.to_string().parse::().unwrap_or(5) as f64) / 100.0; + // No explicit cap → default the RAM budget to detected system RAM. + if mc == 0 && ram <= 0.0 { + match system_ram_gib() { + Some(g) => { + eprintln!( + "[rs_shard] no cap given; defaulting to detected system RAM: {g:.0} GiB" + ); + ram = g; + }, + None => { + return LeanIOResult::error_string( + "could not detect system RAM; pass --max-ram G or --max-cycles N", + ); + }, + } + } + let cap = + if ram > 0.0 { ix_kernel::shard::cycle_cap_for_ram(ram) } else { mc }; + if cap == 0 { + return LeanIOResult::error_string( + "shard cap: RAM budget too small (must clear the ~50 GiB prover base)", + ); + } + let out = out_path.to_string(); + let out_opt = if out.is_empty() { None } else { Some(out.as_str()) }; + match ix_kernel::shard::shard_esp_cap( + &esp_path.to_string(), + cap, + balance, + parallelism, + out_opt, + ) { + Ok(report) => { + eprintln!("[rs_shard]\n{report}"); + LeanIOResult::ok(LeanOwned::box_usize(0)) + }, + Err(e) => LeanIOResult::error_string(&format!("rs_shard_esp_cap: {e}")), + } +} + #[cfg(test)] mod tests { use super::{compact_in_flight_label, resolve_kernel_check_workers_from}; @@ -3072,7 +3140,7 @@ pub extern "C" fn rs_kernel_roundtrip( // Build a plain Lean `Env` from decompile's DashMap for the standard // compare_envs / find_diff flow. let t5 = Instant::now(); - let mut decompiled_env = crate::ix::env::Env::default(); + let mut decompiled_env = ix_common::env::Env::default(); for entry in dstt.env.iter() { decompiled_env.insert(entry.key().clone(), entry.value().clone()); } @@ -3108,10 +3176,10 @@ pub extern "C" fn rs_kernel_roundtrip( /// manageable. #[cfg(feature = "test-ffi")] fn compare_envs( - original: &crate::ix::env::Env, - egressed: &crate::ix::env::Env, + original: &ix_common::env::Env, + egressed: &ix_common::env::Env, ) -> (Vec, usize, usize) { - use crate::ix::env::ConstantInfo as LCI; + use ix_common::env::ConstantInfo as LCI; let total = original.len(); let mut errors: Vec = Vec::new(); @@ -3181,11 +3249,11 @@ fn compare_envs( /// Returns a path-annotated description of where the mismatch is. #[cfg(feature = "test-ffi")] fn find_diff( - a: &crate::ix::env::Expr, - b: &crate::ix::env::Expr, + a: &ix_common::env::Expr, + b: &ix_common::env::Expr, path: &str, ) -> String { - use crate::ix::env::ExprData; + use ix_common::env::ExprData; if a.get_hash() == b.get_hash() { return format!("{path}: hashes match (ok)"); @@ -3281,7 +3349,7 @@ fn find_diff( let mut val_diffs = Vec::new(); for (i, ((n1, v1), (_, v2))) in kvs1.iter().zip(kvs2.iter()).enumerate() { - use crate::ix::env::hash_data_value; + use ix_common::env::hash_data_value; let mut h1 = blake3::Hasher::new(); let mut h2 = blake3::Hasher::new(); hash_data_value(v1, &mut h1); diff --git a/src/lean.rs b/crates/ffi/src/lean.rs similarity index 100% rename from src/lean.rs rename to crates/ffi/src/lean.rs diff --git a/src/ffi/lean_env.rs b/crates/ffi/src/lean_env.rs similarity index 94% rename from src/ffi/lean_env.rs rename to crates/ffi/src/lean_env.rs index 13296fa94..1bd907d33 100644 --- a/src/ffi/lean_env.rs +++ b/crates/ffi/src/lean_env.rs @@ -2,7 +2,7 @@ //! //! Provides functions to walk Lean object pointers and decode them into //! the Rust `Name`, `Level`, `Expr`, and `ConstantInfo` types defined in -//! `crate::ix::env`. Used by the compilation pipeline to read the Lean +//! `ix_common::env`. Used by the compilation pipeline to read the Lean //! environment before transforming it to Ixon format. //! //! Uses a two-level cache (`GlobalCache` + `LocalCache`) to avoid redundant @@ -17,11 +17,12 @@ use rayon::prelude::*; use rustc_hash::FxHashMap; -use crate::ix::compile::{CompileOptions, compile_env_with_options}; -use crate::ix::decompile::{check_decompile, decompile_env}; +use ix_compile::compile::{CompileOptions, compile_env_with_options}; +use ix_compile::decompile::{check_decompile, decompile_env}; use std::sync::Arc; use lean_ffi::nat::Nat; +use lean_ffi::object::LeanNat; use lean_ffi::object::{ LeanArray, LeanBorrowed, LeanList, LeanRef, LeanShared, }; @@ -35,7 +36,7 @@ use crate::lean::{ LeanIxSyntaxPreresolved, LeanIxTheoremVal, }; -use crate::ix::env::{ +use ix_common::env::{ AxiomVal, BinderInfo, ConstantInfo, ConstantVal, ConstructorVal, DataValue, DefinitionSafety, DefinitionVal, Env, Expr, InductiveVal, Int, Level, Literal, Name, OpaqueVal, QuotKind, QuotVal, RecursorRule, RecursorVal, @@ -55,7 +56,7 @@ const PARALLEL_THRESHOLD: usize = 100; /// generated recursor is intentionally canonical rather than source-identical. fn primary_addresses_collapse( all: &[Name], - stt: &crate::ix::compile::CompileState, + stt: &ix_compile::compile::CompileState, ) -> bool { let mut seen = rustc_hash::FxHashSet::default(); for name in all { @@ -72,12 +73,12 @@ fn primary_addresses_collapse( fn build_aux_perm_ctx( all: &[Name], env: &Env, - stt: &crate::ix::compile::CompileState, + stt: &ix_compile::compile::CompileState, perm: &[usize], -) -> Option { - use crate::ix::compile::aux_gen; - use crate::ix::congruence::perm::{PermCtx, RecHeadInfo, RecHeadKind}; - use crate::ix::env::{ConstantInfo as LeanCI, ExprData}; +) -> Option { + use ix_common::env::{ConstantInfo as LeanCI, ExprData}; + use ix_compile::compile::aux_gen; + use ix_compile::congruence::perm::{PermCtx, RecHeadInfo, RecHeadKind}; let first = all.first()?; let n_params = match env.get(first) { @@ -162,7 +163,7 @@ fn build_aux_perm_ctx( ); } - let mut const_addr: FxHashMap = + let mut const_addr: FxHashMap = FxHashMap::default(); let mut add_addr = |name: &Name| { if let Some(addr) = stt.resolve_addr(name) { @@ -194,8 +195,8 @@ fn build_aux_perm_ctx( fn collect_const_addrs( e: &Expr, - stt: &crate::ix::compile::CompileState, - out: &mut FxHashMap, + stt: &ix_compile::compile::CompileState, + out: &mut FxHashMap, ) { match e.as_data() { ExprData::Const(n, _, _) => { @@ -261,13 +262,13 @@ fn build_aux_perm_ctx( fn build_collapse_const_map( all: &[Name], env: &Env, - stt: &crate::ix::compile::CompileState, + stt: &ix_compile::compile::CompileState, ) -> FxHashMap { - use crate::ix::env::ConstantInfo as LeanCI; + use ix_common::env::ConstantInfo as LeanCI; let mut map: FxHashMap = FxHashMap::default(); // Group primary members by canonical address; the first member with a // given address is the representative. - let mut rep_by_addr: FxHashMap = + let mut rep_by_addr: FxHashMap = FxHashMap::default(); for member in all { let Some(addr) = stt.resolve_addr(member) else { @@ -323,21 +324,21 @@ fn build_collapse_const_map( #[derive(Clone)] struct AuxCompareEntry { generated: ConstantInfo, - ctx: Option, + ctx: Option, } fn aux_patch_to_lean_ci( - patch: &crate::ix::compile::aux_gen::PatchedConstant, -) -> Option { - use crate::ix::env::{ + patch: &ix_compile::compile::aux_gen::PatchedConstant, +) -> ConstantInfo { + use ix_common::env::{ ConstantInfo as LeanCI, ConstantVal as LeanCV, DefinitionVal, InductiveVal, }; - Some(match patch { - crate::ix::compile::aux_gen::PatchedConstant::Rec(r) => { + match patch { + ix_compile::compile::aux_gen::PatchedConstant::Rec(r) => { LeanCI::RecInfo(r.clone()) }, - crate::ix::compile::aux_gen::PatchedConstant::CasesOn(d) - | crate::ix::compile::aux_gen::PatchedConstant::RecOn(d) => { + ix_compile::compile::aux_gen::PatchedConstant::CasesOn(d) + | ix_compile::compile::aux_gen::PatchedConstant::RecOn(d) => { LeanCI::DefnInfo(DefinitionVal { cnst: LeanCV { name: d.name.clone(), @@ -350,7 +351,7 @@ fn aux_patch_to_lean_ci( all: vec![], }) }, - crate::ix::compile::aux_gen::PatchedConstant::BelowDef(d) => { + ix_compile::compile::aux_gen::PatchedConstant::BelowDef(d) => { LeanCI::DefnInfo(DefinitionVal { cnst: LeanCV { name: d.name.clone(), @@ -363,7 +364,7 @@ fn aux_patch_to_lean_ci( all: vec![], }) }, - crate::ix::compile::aux_gen::PatchedConstant::BRecOn(d) => { + ix_compile::compile::aux_gen::PatchedConstant::BRecOn(d) => { LeanCI::DefnInfo(DefinitionVal { cnst: LeanCV { name: d.name.clone(), @@ -376,7 +377,7 @@ fn aux_patch_to_lean_ci( all: vec![], }) }, - crate::ix::compile::aux_gen::PatchedConstant::BelowIndc(bi) => { + ix_compile::compile::aux_gen::PatchedConstant::BelowIndc(bi) => { LeanCI::InductInfo(InductiveVal { cnst: LeanCV { name: bi.name.clone(), @@ -393,7 +394,7 @@ fn aux_patch_to_lean_ci( is_reflexive: bi.is_reflexive, }) }, - }) + } } fn aux_congruence_result( @@ -402,8 +403,8 @@ fn aux_congruence_result( original: &ConstantInfo, entry: Option<&AuxCompareEntry>, ) -> Result<(), String> { - use crate::ix::congruence::const_alpha_eq; - use crate::ix::congruence::perm::const_alpha_eq_with_perm; + use ix_compile::congruence::const_alpha_eq; + use ix_compile::congruence::perm::const_alpha_eq_with_perm; if let Ok(()) = const_alpha_eq(decompiled, original) { return Ok(()); } @@ -478,11 +479,11 @@ fn aux_congruence_result( fn build_aux_compare_contexts( env: &Arc, - stt: &crate::ix::compile::CompileState, + stt: &ix_compile::compile::CompileState, ) -> FxHashMap { - use crate::ix::compile::KernelCtx; - use crate::ix::compile::aux_gen::{self, expr_utils}; - use crate::ix::env::ConstantInfo as LeanCI; + use ix_common::env::ConstantInfo as LeanCI; + use ix_compile::compile::KernelCtx; + use ix_compile::compile::aux_gen::{self, expr_utils}; use rustc_hash::FxHashSet; let mut by_name = FxHashMap::default(); @@ -523,12 +524,11 @@ fn build_aux_compare_contexts( None }; for (patch_name, patch) in aux_out.patches.iter() { - if let Some(generated) = aux_patch_to_lean_ci(patch) { - by_name.insert( - patch_name.clone(), - AuxCompareEntry { generated, ctx: ctx.clone() }, - ); - } + let generated = aux_patch_to_lean_ci(patch); + by_name.insert( + patch_name.clone(), + AuxCompareEntry { generated, ctx: ctx.clone() }, + ); } } by_name @@ -610,7 +610,7 @@ pub fn decode_name(obj: LeanBorrowed<'_>, global: &GlobalCache) -> Name { let pos = n.get_obj(1); match n.as_ctor().tag() { 1 => Name::str(pre, pos.as_string().to_string()), - 2 => Name::num(pre, Nat::from_obj(&pos)), + 2 => Name::num(pre, LeanNat::to_nat(&pos)), tag => unreachable!("Invalid Lean.Name tag: {tag}"), } }; @@ -668,8 +668,8 @@ fn decode_level(obj: LeanBorrowed<'_>, cache: &mut Cache<'_>) -> Level { fn decode_substring(obj: LeanBorrowed<'_>) -> Substring { let s = LeanIxSubstring::from_ctor(obj.as_ctor()); let str = s.get_obj(0).as_string().to_string(); - let start_pos = Nat::from_obj(&s.get_obj(1)); - let stop_pos = Nat::from_obj(&s.get_obj(2)); + let start_pos = LeanNat::to_nat(&s.get_obj(1)); + let stop_pos = LeanNat::to_nat(&s.get_obj(2)); Substring { str, start_pos, stop_pos } } @@ -681,14 +681,14 @@ fn decode_source_info(obj: LeanBorrowed<'_>) -> SourceInfo { match si.as_ctor().tag() { 0 => { let leading = decode_substring(si.get_obj(0)); - let pos = Nat::from_obj(&si.get_obj(1)); + let pos = LeanNat::to_nat(&si.get_obj(1)); let trailing = decode_substring(si.get_obj(2)); - let end_pos = Nat::from_obj(&si.get_obj(3)); + let end_pos = LeanNat::to_nat(&si.get_obj(3)); SourceInfo::Original(leading, pos, trailing, end_pos) }, 1 => { - let pos = Nat::from_obj(&si.get_obj(0)); - let end_pos = Nat::from_obj(&si.get_obj(1)); + let pos = LeanNat::to_nat(&si.get_obj(0)); + let end_pos = LeanNat::to_nat(&si.get_obj(1)); let canonical = si.get_num_8(0) != 0; SourceInfo::Synthetic(pos, end_pos, canonical) }, @@ -768,10 +768,10 @@ fn decode_name_data_value( 0 => DataValue::OfString(dv.get_obj(0).as_string().to_string()), 1 => DataValue::OfBool(dv.get_num_8(0) != 0), 2 => DataValue::OfName(decode_name(dv.get_obj(0), cache.global)), - 3 => DataValue::OfNat(Nat::from_obj(&dv.get_obj(0))), + 3 => DataValue::OfNat(LeanNat::to_nat(&dv.get_obj(0))), 4 => { let i = LeanIxInt::from_ctor(dv.get_obj(0).as_ctor()); - let nat = Nat::from_obj(&i.get_obj(0)); + let nat = LeanNat::to_nat(&i.get_obj(0)); let int = match i.as_ctor().tag() { 0 => Int::OfNat(nat), 1 => Int::NegSucc(nat), @@ -799,7 +799,7 @@ pub fn decode_expr(obj: LeanBorrowed<'_>, cache: &mut Cache<'_>) -> Expr { _ => unreachable!("Invalid Lean.BinderInfo tag: {b}"), }; let expr = match e.as_ctor().tag() { - 0 => Expr::bvar(Nat::from_obj(&e.get_obj(0))), + 0 => Expr::bvar(LeanNat::to_nat(&e.get_obj(0))), 1 => Expr::fvar(decode_name(e.get_obj(0), cache.global)), 2 => Expr::mvar(decode_name(e.get_obj(0), cache.global)), 3 => Expr::sort(decode_level(e.get_obj(0), cache)), @@ -842,7 +842,7 @@ pub fn decode_expr(obj: LeanBorrowed<'_>, cache: &mut Cache<'_>) -> Expr { let lit = LeanIxLiteral::from_ctor(e.get_obj(0).as_ctor()); let inner = lit.get_obj(0); match lit.as_ctor().tag() { - 0 => Expr::lit(Literal::NatVal(Nat::from_obj(&inner))), + 0 => Expr::lit(Literal::NatVal(LeanNat::to_nat(&inner))), 1 => Expr::lit(Literal::StrVal(inner.as_string().to_string())), tag => unreachable!("Invalid Lean.Literal tag: {tag}"), } @@ -857,7 +857,7 @@ pub fn decode_expr(obj: LeanBorrowed<'_>, cache: &mut Cache<'_>) -> Expr { }, 11 => { let typ_name = decode_name(e.get_obj(0), cache.global); - let idx = Nat::from_obj(&e.get_obj(1)); + let idx = LeanNat::to_nat(&e.get_obj(1)); let struct_expr = decode_expr(e.get_obj(2), cache); Expr::proj(typ_name, idx, struct_expr) }, @@ -873,7 +873,7 @@ fn decode_recursor_rule( ) -> RecursorRule { let r = LeanIxRecursorRule::from_ctor(obj.as_ctor()); let ctor_name = decode_name(r.get_obj(0), cache.global); - let n_fields = Nat::from_obj(&r.get_obj(1)); + let n_fields = LeanNat::to_nat(&r.get_obj(1)); let rhs = decode_expr(r.get_obj(2), cache); RecursorRule { ctor: ctor_name, n_fields, rhs } } @@ -980,8 +980,8 @@ pub fn decode_constant_info( 5 => { let inner = LeanIxInductiveVal::from_ctor(inner_obj.as_ctor()); let constant_val = decode_constant_val(inner.get_obj(0), cache); - let num_params = Nat::from_obj(&inner.get_obj(1)); - let num_indices = Nat::from_obj(&inner.get_obj(2)); + let num_params = LeanNat::to_nat(&inner.get_obj(1)); + let num_indices = LeanNat::to_nat(&inner.get_obj(2)); let all: Vec<_> = collect_list_borrowed(inner.get_obj(3).as_list()) .into_iter() .map(|o| decode_name(o, cache.global)) @@ -990,7 +990,7 @@ pub fn decode_constant_info( .into_iter() .map(|o| decode_name(o, cache.global)) .collect(); - let num_nested = Nat::from_obj(&inner.get_obj(5)); + let num_nested = LeanNat::to_nat(&inner.get_obj(5)); let is_rec = inner.get_num_8(0) != 0; let is_unsafe = inner.get_num_8(1) != 0; let is_reflexive = inner.get_num_8(2) != 0; @@ -1010,9 +1010,9 @@ pub fn decode_constant_info( let inner = LeanIxConstructorVal::from_ctor(inner_obj.as_ctor()); let constant_val = decode_constant_val(inner.get_obj(0), cache); let induct = decode_name(inner.get_obj(1), cache.global); - let cidx = Nat::from_obj(&inner.get_obj(2)); - let num_params = Nat::from_obj(&inner.get_obj(3)); - let num_fields = Nat::from_obj(&inner.get_obj(4)); + let cidx = LeanNat::to_nat(&inner.get_obj(2)); + let num_params = LeanNat::to_nat(&inner.get_obj(3)); + let num_fields = LeanNat::to_nat(&inner.get_obj(4)); let is_unsafe = inner.get_num_8(0) != 0; ConstantInfo::CtorInfo(ConstructorVal { cnst: constant_val, @@ -1030,10 +1030,10 @@ pub fn decode_constant_info( .into_iter() .map(|o| decode_name(o, cache.global)) .collect(); - let num_params = Nat::from_obj(&inner.get_obj(2)); - let num_indices = Nat::from_obj(&inner.get_obj(3)); - let num_motives = Nat::from_obj(&inner.get_obj(4)); - let num_minors = Nat::from_obj(&inner.get_obj(5)); + let num_params = LeanNat::to_nat(&inner.get_obj(2)); + let num_indices = LeanNat::to_nat(&inner.get_obj(3)); + let num_motives = LeanNat::to_nat(&inner.get_obj(4)); + let num_minors = LeanNat::to_nat(&inner.get_obj(5)); let rules: Vec<_> = collect_list_borrowed(inner.get_obj(6).as_list()) .into_iter() .map(|o| decode_recursor_rule(o, cache)) @@ -1117,11 +1117,11 @@ extern "C" fn rs_tmp_decode_const_map( obj: LeanList>, ) -> usize { // Enable hash-consed size tracking for debugging - crate::ix::compile::TRACK_HASH_CONSED_SIZE + ix_compile::compile::TRACK_HASH_CONSED_SIZE .store(true, std::sync::atomic::Ordering::Relaxed); // Enable verbose sharing analysis for debugging pathological blocks - crate::ix::compile::ANALYZE_SHARING + ix_compile::compile::ANALYZE_SHARING .store(false, std::sync::atomic::Ordering::Relaxed); let env = decode_env(obj); @@ -1150,12 +1150,12 @@ extern "C" fn rs_tmp_decode_const_map( // Phase 1b: Aux_gen congruence (full env) eprintln!("[rust-compile] Phase 1b: Checking aux_gen congruence..."); { - use crate::ix::compile::aux_gen::{self, PatchedConstant, expr_utils}; - use crate::ix::congruence::const_alpha_eq; - use crate::ix::env::{ + use ix_common::env::{ ConstantInfo as LeanCI, ConstantVal as LeanCV, DefinitionSafety, DefinitionVal, InductiveVal, ReducibilityHints, }; + use ix_compile::compile::aux_gen::{self, PatchedConstant, expr_utils}; + use ix_compile::congruence::const_alpha_eq; use rustc_hash::{FxHashMap, FxHashSet}; // Build per-block PermCtx for the permutation-aware comparator. @@ -1165,11 +1165,11 @@ extern "C" fn rs_tmp_decode_const_map( fn build_perm_ctx_1b( all: &[Name], env: &Env, - stt: &crate::ix::compile::CompileState, + stt: &ix_compile::compile::CompileState, perm: &[usize], - ) -> Option { - use crate::ix::congruence::perm::{PermCtx, RecHeadInfo, RecHeadKind}; - use crate::ix::env::{ConstantInfo as LeanCI, ExprData}; + ) -> Option { + use ix_common::env::{ConstantInfo as LeanCI, ExprData}; + use ix_compile::congruence::perm::{PermCtx, RecHeadInfo, RecHeadKind}; let first = all.first()?; let n_params = match env.get(first) { @@ -1257,7 +1257,7 @@ extern "C" fn rs_tmp_decode_const_map( ); } - let mut const_addr: FxHashMap = + let mut const_addr: FxHashMap = FxHashMap::default(); let mut add_addr = |name: &Name| { if let Some(addr) = stt.resolve_addr(name) { @@ -1290,8 +1290,8 @@ extern "C" fn rs_tmp_decode_const_map( } fn collect_const_addrs( e: &Expr, - stt: &crate::ix::compile::CompileState, - out: &mut FxHashMap, + stt: &ix_compile::compile::CompileState, + out: &mut FxHashMap, ) { match e.as_data() { ExprData::Const(n, _, _) => { @@ -1377,7 +1377,7 @@ extern "C" fn rs_tmp_decode_const_map( continue; } - let mut local_kctx = crate::ix::compile::KernelCtx::new(); + let mut local_kctx = ix_compile::compile::KernelCtx::new(); expr_utils::ensure_prelude_in_kenv_of(&stt, &mut local_kctx); let orig_aux_out = match aux_gen::generate_aux_patches( &original_classes, @@ -1405,7 +1405,7 @@ extern "C" fn rs_tmp_decode_const_map( // `rs_compile_validate_aux`) for the full builder; the // `#[cfg(feature = "test-ffi")]` Phase 1b path here uses a // local copy with the same logic. - let perm_ctx_1b: Option = + let perm_ctx_1b: Option = if let Some(perm) = &orig_aux_out.perm && !perm.is_empty() { @@ -1475,7 +1475,7 @@ extern "C" fn rs_tmp_decode_const_map( }; let orig_ci: &LeanCI = orig_ci_ref; let eq_result = match &perm_ctx_1b { - Some(ctx) => crate::ix::congruence::perm::const_alpha_eq_with_perm( + Some(ctx) => ix_compile::congruence::perm::const_alpha_eq_with_perm( &gen_ci, orig_ci, ctx, ), None => const_alpha_eq(&gen_ci, orig_ci), @@ -1598,9 +1598,9 @@ extern "C" fn rs_tmp_decode_const_map( eprintln!("[rust-compile] Phase 6: Deserializing and re-decompiling..."); let t4 = std::time::Instant::now(); let mut buf: &[u8] = &serialized; - match crate::ix::ixon::env::Env::get(&mut buf) { + match ixon::env::Env::get(&mut buf) { Ok(fresh_env) => { - let fresh_stt = crate::ix::compile::CompileState { + let fresh_stt = ix_compile::compile::CompileState { env: fresh_env, ..Default::default() }; @@ -1690,7 +1690,7 @@ impl PhaseResult { extern "C" fn rs_compile_validate_aux( obj: LeanList>, ) -> usize { - use crate::ix::congruence::const_alpha_eq; + use ix_compile::congruence::const_alpha_eq; use rustc_hash::FxHashSet; let t_total = std::time::Instant::now(); @@ -1796,10 +1796,10 @@ extern "C" fn rs_compile_validate_aux( let mut p2 = PhaseResult::new("2. Aux_gen congruence"); println!("{VALIDATE_PREFIX} phase 2: checking aux_gen congruence..."); { - use crate::ix::compile::aux_gen::{self, PatchedConstant, expr_utils}; - use crate::ix::compile::{KernelCtx, mk_indc}; - use crate::ix::env::ConstantInfo as LeanCI; - use crate::ix::mutual::MutConst; + use ix_common::env::ConstantInfo as LeanCI; + use ix_compile::compile::aux_gen::{self, PatchedConstant, expr_utils}; + use ix_compile::compile::{KernelCtx, mk_indc}; + use ix_compile::mutual::MutConst; // Ephemeral kernel context for original-structure congruence testing. // Shared across all blocks (accumulates inductives incrementally). @@ -1867,7 +1867,7 @@ extern "C" fn rs_compile_validate_aux( // introducing races (even though individual DashMap inserts are safe, // a reader may observe a partially-ingressed kctx and fail). { - use crate::ix::graph::get_constant_info_references; + use ix_compile::graph::get_constant_info_references; // Step A (serial): enumerate the transitive-closure of names to // ingress. BFS walking the env hashmap is cheap — the per-node cost // is a lookup and a ref-walk, dwarfed by Step B's actual ingress. @@ -1913,18 +1913,18 @@ extern "C" fn rs_compile_validate_aux( // Build a `PermCtx` for the block: the congruence comparator uses // it to walk gen vs orig in lockstep with permutation awareness. - // See `crate::ix::congruence::perm` for details. + // See `ix_compile::congruence::perm` for details. // // `n_primary = all.len()` because Phase 2 uses singleton classes // (one class per original, no alpha-collapse at the primary level). fn build_perm_ctx( all: &[Name], env: &Env, - stt: &crate::ix::compile::CompileState, + stt: &ix_compile::compile::CompileState, perm: &[usize], - ) -> Option { - use crate::ix::congruence::perm::{PermCtx, RecHeadInfo}; - use crate::ix::env::ConstantInfo as LeanCI; + ) -> Option { + use ix_common::env::ConstantInfo as LeanCI; + use ix_compile::congruence::perm::{PermCtx, RecHeadInfo}; use rustc_hash::FxHashMap; let first = all.first()?; @@ -1968,7 +1968,7 @@ extern "C" fn rs_compile_validate_aux( // - Aux `.below_N` (kind = Below) — `{first}.below_{N}` // - Primary `.brecOn`/.go/.eq (kind = BRecOn) // - Aux `.brecOn_N`/.go/.eq (kind = BRecOn) - use crate::ix::congruence::perm::RecHeadKind; + use ix_compile::congruence::perm::RecHeadKind; let n_motives = n_primary + source_aux_ctor_counts.len(); let n_minors: usize = primary_ctor_counts.iter().sum::() + source_aux_ctor_counts.iter().sum::(); @@ -2049,7 +2049,7 @@ extern "C" fn rs_compile_validate_aux( // operate on collapsed blocks pick up the rewrites automatically. // (Built below at the PermCtx construction site so `env`/`stt` // borrows don't conflict with the const_addr-collecting closure.) - let mut const_addr: FxHashMap = + let mut const_addr: FxHashMap = FxHashMap::default(); let mut add_addr = |name: &Name| { if let Some(addr) = stt.resolve_addr(name) { @@ -2084,10 +2084,10 @@ extern "C" fn rs_compile_validate_aux( } fn collect_const_addrs( e: &Expr, - stt: &crate::ix::compile::CompileState, - out: &mut FxHashMap, + stt: &ix_compile::compile::CompileState, + out: &mut FxHashMap, ) { - use crate::ix::env::ExprData; + use ix_common::env::ExprData; match e.as_data() { ExprData::Const(n, _, _) => { if let Some(addr) = stt.resolve_addr(n) { @@ -2145,12 +2145,12 @@ extern "C" fn rs_compile_validate_aux( } // Helper to wrap a patch as a Lean `ConstantInfo` for alpha-eq. - fn patch_to_lean_ci(patch: &PatchedConstant) -> Option { - use crate::ix::env::{ + fn patch_to_lean_ci(patch: &PatchedConstant) -> ConstantInfo { + use ix_common::env::{ ConstantInfo as LeanCI, ConstantVal as LeanCV, DefinitionSafety, DefinitionVal, InductiveVal, ReducibilityHints, }; - Some(match patch { + match patch { PatchedConstant::Rec(r) => LeanCI::RecInfo(r.clone()), PatchedConstant::CasesOn(d) | PatchedConstant::RecOn(d) => { LeanCI::DefnInfo(DefinitionVal { @@ -2202,7 +2202,7 @@ extern "C" fn rs_compile_validate_aux( is_unsafe: false, is_reflexive: bi.is_reflexive, }), - }) + } } // Diagnostic dump printed per-thread on alpha-eq failure. Writes go @@ -2215,7 +2215,7 @@ extern "C" fn rs_compile_validate_aux( orig_ci: &ConstantInfo, err: &str, ) { - use crate::ix::env::{Expr, ExprData as ED}; + use ix_common::env::{Expr, ExprData as ED}; fn extract_sort(e: &Expr, depth: usize) -> String { match e.as_data() { @@ -2268,7 +2268,7 @@ extern "C" fn rs_compile_validate_aux( // Build a PermCtx for this block once. When the block has no // nested auxes (`perm == None` or empty), we pass `None` and // fall through to plain `const_alpha_eq`. - let perm_ctx: Option = + let perm_ctx: Option = if let Some(p) = &orig_aux_out.perm && !p.is_empty() { @@ -2282,16 +2282,18 @@ extern "C" fn rs_compile_validate_aux( let mut result = BlockResult::default(); let mut dumped = 0usize; for (patch_name, patch) in orig_patches.iter() { - let Some(gen_ci) = patch_to_lean_ci(patch) else { continue }; + let gen_ci = patch_to_lean_ci(patch); let Some(orig_ci_ref) = env.get(patch_name) else { continue; // Synthetic name — no Lean original. }; let orig_ci: &LeanCI = orig_ci_ref; let eq_result = match &perm_ctx { - Some(ctx) => crate::ix::congruence::perm::const_alpha_eq_with_perm( - &gen_ci, orig_ci, ctx, - ), + Some(ctx) => { + ix_compile::congruence::perm::const_alpha_eq_with_perm( + &gen_ci, orig_ci, ctx, + ) + }, None => const_alpha_eq(&gen_ci, orig_ci), }; @@ -2333,7 +2335,7 @@ extern "C" fn rs_compile_validate_aux( // Precompute canonical addresses: any orig_addr that matches another Named // entry's canonical addr is in consts legitimately (not an ephemeral leak). // The gather itself parallelizes cleanly over the DashMap. - let canonical_addrs: FxHashSet = + let canonical_addrs: FxHashSet = stt.env.named.par_iter().map(|e| e.value().addr.clone()).collect(); // Parallel scan over named DashMap. Each check is read-only against @@ -2463,17 +2465,17 @@ extern "C" fn rs_compile_validate_aux( } fn describe_addr( - stt: &crate::ix::compile::CompileState, - addr: &crate::ix::address::Address, + stt: &ix_compile::compile::CompileState, + addr: &ix_common::address::Address, ) -> String { match stt.env.get_const(addr).map(|c| c.info.clone()) { - Some(crate::ix::ixon::constant::ConstantInfo::RPrj(p)) => { + Some(ixon::constant::ConstantInfo::RPrj(p)) => { format!("RPrj(idx={}, block={:.12})", p.idx, p.block.hex()) }, - Some(crate::ix::ixon::constant::ConstantInfo::IPrj(p)) => { + Some(ixon::constant::ConstantInfo::IPrj(p)) => { format!("IPrj(idx={}, block={:.12})", p.idx, p.block.hex()) }, - Some(crate::ix::ixon::constant::ConstantInfo::CPrj(p)) => { + Some(ixon::constant::ConstantInfo::CPrj(p)) => { format!( "CPrj(idx={}, cidx={}, block={:.12})", p.idx, @@ -2487,14 +2489,14 @@ extern "C" fn rs_compile_validate_aux( } fn describe_rprj_block( - stt: &crate::ix::compile::CompileState, - addr: &crate::ix::address::Address, + stt: &ix_compile::compile::CompileState, + addr: &ix_common::address::Address, ) -> Option { fn expand_shares_expr( - expr: &Arc, - sharing: &[Arc], - ) -> Arc { - use crate::ix::ixon::expr::Expr; + expr: &Arc, + sharing: &[Arc], + ) -> Arc { + use ixon::expr::Expr; match expr.as_ref() { Expr::Share(idx) => sharing.get(*idx as usize).map_or_else( || expr.clone(), @@ -2528,10 +2530,10 @@ extern "C" fn rs_compile_validate_aux( } fn expand_shares_member( - member: &crate::ix::ixon::constant::MutConst, - sharing: &[Arc], - ) -> crate::ix::ixon::constant::MutConst { - use crate::ix::ixon::constant::{MutConst, RecursorRule}; + member: &ixon::constant::MutConst, + sharing: &[Arc], + ) -> ixon::constant::MutConst { + use ixon::constant::{MutConst, RecursorRule}; match member { MutConst::Defn(def) => { let mut def = def.clone(); @@ -2563,18 +2565,18 @@ extern "C" fn rs_compile_validate_aux( } } - fn expr_hash_prefix(expr: &Arc) -> String { + fn expr_hash_prefix(expr: &Arc) -> String { let mut buf = Vec::new(); - crate::ix::ixon::serialize::put_expr(expr, &mut buf); - let h = crate::ix::address::Address::hash(&buf); + ixon::serialize::put_expr(expr, &mut buf); + let h = ix_common::address::Address::hash(&buf); format!("{}:{}", buf.len(), &h.hex()[..12]) } fn member_parts_summary( - member: &crate::ix::ixon::constant::MutConst, - sharing: &[Arc], + member: &ixon::constant::MutConst, + sharing: &[Arc], ) -> String { - use crate::ix::ixon::constant::MutConst; + use ixon::constant::MutConst; let expanded = expand_shares_member(member, sharing); match expanded { MutConst::Defn(def) => { @@ -2598,12 +2600,12 @@ extern "C" fn rs_compile_validate_aux( } let proj = match stt.env.get_const(addr).map(|c| c.info.clone()) { - Some(crate::ix::ixon::constant::ConstantInfo::RPrj(p)) => p, + Some(ixon::constant::ConstantInfo::RPrj(p)) => p, _ => return None, }; let block = stt.env.get_const(&proj.block)?; let member_count_for_names = match &block.info { - crate::ix::ixon::constant::ConstantInfo::Muts(ms) => ms.len(), + ixon::constant::ConstantInfo::Muts(ms) => ms.len(), _ => 0, }; let proj_names: Vec = (0..member_count_for_names) @@ -2615,17 +2617,17 @@ extern "C" fn rs_compile_validate_aux( .chain(stt.name_to_addr.iter()) .filter_map(|entry| { match stt.env.get_const(entry.value()).map(|c| c.info.clone()) { - Some(crate::ix::ixon::constant::ConstantInfo::RPrj(p)) + Some(ixon::constant::ConstantInfo::RPrj(p)) if p.block == proj.block && p.idx == idx => { Some(entry.key().pretty()) }, - Some(crate::ix::ixon::constant::ConstantInfo::IPrj(p)) + Some(ixon::constant::ConstantInfo::IPrj(p)) if p.block == proj.block && p.idx == idx => { Some(entry.key().pretty()) }, - Some(crate::ix::ixon::constant::ConstantInfo::DPrj(p)) + Some(ixon::constant::ConstantInfo::DPrj(p)) if p.block == proj.block && p.idx == idx => { Some(entry.key().pretty()) @@ -2659,22 +2661,22 @@ extern "C" fn rs_compile_validate_aux( }) .collect(); let (members, per_member_hashes) = match &block.info { - crate::ix::ixon::constant::ConstantInfo::Muts(ms) => { + ixon::constant::ConstantInfo::Muts(ms) => { let per: Vec = ms .iter() .map(|m| { // Compute a per-member byte hash for quick diffing. let mut buf = Vec::new(); m.put(&mut buf); - let h = crate::ix::address::Address::hash(&buf); + let h = ix_common::address::Address::hash(&buf); let expanded = expand_shares_member(m, &block.sharing); let mut expanded_buf = Vec::new(); expanded.put(&mut expanded_buf); - let expanded_h = crate::ix::address::Address::hash(&expanded_buf); + let expanded_h = ix_common::address::Address::hash(&expanded_buf); let tag = match m { - crate::ix::ixon::constant::MutConst::Defn(_) => "Defn", - crate::ix::ixon::constant::MutConst::Indc(_) => "Indc", - crate::ix::ixon::constant::MutConst::Recr(_) => "Recr", + ixon::constant::MutConst::Defn(_) => "Defn", + ixon::constant::MutConst::Indc(_) => "Indc", + ixon::constant::MutConst::Recr(_) => "Recr", }; let parts = member_parts_summary(m, &block.sharing); format!( @@ -3563,7 +3565,7 @@ extern "C" fn rs_compile_validate_aux( // ends before we drop it. let fresh_env = { let mut buf: &[u8] = &serialized; - match crate::ix::ixon::env::Env::get(&mut buf) { + match ixon::env::Env::get(&mut buf) { Ok(fe) => Some(fe), Err(e) => { p7.record_fail(format!("deserialize FAILED: {e}")); @@ -3576,7 +3578,7 @@ extern "C" fn rs_compile_validate_aux( match fresh_env { Some(fresh_env) => { - let fresh_stt = crate::ix::compile::CompileState { + let fresh_stt = ix_compile::compile::CompileState { env: fresh_env, ..Default::default() }; @@ -3672,7 +3674,7 @@ extern "C" fn rs_compile_validate_aux( (None, None) => true, _ => false, }; - let aux_eq_result = if crate::ix::decompile::is_aux_gen_suffix(name) + let aux_eq_result = if ix_compile::decompile::is_aux_gen_suffix(name) && !(type_ok && val_ok) { Some(aux_congruence_result( @@ -3738,8 +3740,8 @@ extern "C" fn rs_compile_validate_aux( // ══════════════════════════════════════════════════════════════════════ let mut p8 = PhaseResult::new("8. Nested detection"); { - use crate::ix::compile::aux_gen::nested::build_compile_flat_block; - use crate::ix::env::ConstantInfo; + use ix_common::env::ConstantInfo; + use ix_compile::compile::aux_gen::nested::build_compile_flat_block; /// Build a dotted Lean name from a dot-separated string. /// Numeric components (e.g. the `0` in `_private.Foo.0.Bar`) are @@ -3880,7 +3882,7 @@ impl ConstSizeBreakdown { #[cfg(feature = "test-ffi")] /// Analyze the serialized size of a constant and its transitive dependencies. -fn analyze_const_size(stt: &crate::ix::compile::CompileState, name_str: &str) { +fn analyze_const_size(stt: &ix_compile::compile::CompileState, name_str: &str) { // Build a global name index for metadata serialization let name_index = build_name_index(stt); @@ -4013,10 +4015,10 @@ fn analyze_const_size(stt: &crate::ix::compile::CompileState, name_str: &str) { /// Build a name index for metadata serialization. #[cfg(feature = "test-ffi")] fn build_name_index( - stt: &crate::ix::compile::CompileState, -) -> crate::ix::ixon::metadata::NameIndex { - use crate::ix::address::Address; - use crate::ix::ixon::metadata::NameIndex; + stt: &ix_compile::compile::CompileState, +) -> ixon::metadata::NameIndex { + use ix_common::address::Address; + use ixon::metadata::NameIndex; let mut idx = NameIndex::new(); let mut counter: u64 = 0; @@ -4037,10 +4039,10 @@ fn build_name_index( /// Compute size breakdown for a constant (alpha-invariant vs metadata). #[cfg(feature = "test-ffi")] fn compute_const_size_breakdown( - constant: &crate::ix::ixon::constant::Constant, + constant: &ixon::constant::Constant, name: &Name, - stt: &crate::ix::compile::CompileState, - name_index: &crate::ix::ixon::metadata::NameIndex, + stt: &ix_compile::compile::CompileState, + name_index: &ixon::metadata::NameIndex, ) -> ConstSizeBreakdown { // Alpha-invariant size let alpha_size = serialized_const_size(constant); @@ -4058,8 +4060,8 @@ fn compute_const_size_breakdown( /// Compute the serialized size of constant metadata. #[cfg(feature = "test-ffi")] fn serialized_meta_size( - meta: &crate::ix::ixon::metadata::ConstantMeta, - name_index: &crate::ix::ixon::metadata::NameIndex, + meta: &ixon::metadata::ConstantMeta, + name_index: &ixon::metadata::NameIndex, ) -> usize { let mut buf = Vec::new(); meta @@ -4088,9 +4090,7 @@ pub fn parse_name(s: &str) -> Name { /// Compute the serialized size of a constant. #[cfg(feature = "test-ffi")] -fn serialized_const_size( - constant: &crate::ix::ixon::constant::Constant, -) -> usize { +fn serialized_const_size(constant: &ixon::constant::Constant) -> usize { let mut buf = Vec::new(); constant.put(&mut buf); buf.len() @@ -4098,11 +4098,11 @@ fn serialized_const_size( /// Analyze block size statistics: hash-consing vs serialization. #[cfg(feature = "test-ffi")] -fn analyze_block_size_stats(stt: &crate::ix::compile::CompileState) { - use crate::ix::compile::BlockSizeStats; +fn analyze_block_size_stats(stt: &ix_compile::compile::CompileState) { + use ix_compile::compile::BlockSizeStats; // Check if hash-consed size tracking was enabled - let tracking_enabled = crate::ix::compile::TRACK_HASH_CONSED_SIZE + let tracking_enabled = ix_compile::compile::TRACK_HASH_CONSED_SIZE .load(std::sync::atomic::Ordering::Relaxed); if !tracking_enabled { println!("\n=== Block Size Analysis ==="); diff --git a/src/ffi/iroh.rs b/crates/ffi/src/lean_iroh.rs similarity index 100% rename from src/ffi/iroh.rs rename to crates/ffi/src/lean_iroh.rs diff --git a/src/ffi/ixon.rs b/crates/ffi/src/lean_ixon.rs similarity index 100% rename from src/ffi/ixon.rs rename to crates/ffi/src/lean_ixon.rs diff --git a/src/ffi/ixon/compare.rs b/crates/ffi/src/lean_ixon/compare.rs similarity index 96% rename from src/ffi/ixon/compare.rs rename to crates/ffi/src/lean_ixon/compare.rs index aa82724bb..6890d89d1 100644 --- a/src/ffi/ixon/compare.rs +++ b/crates/ffi/src/lean_ixon/compare.rs @@ -2,14 +2,16 @@ use std::collections::HashMap; -use crate::ix::compile::{BlockCache, CompileState, compile_env, compile_expr}; -use crate::ix::env::Name; -use crate::ix::ixon::serialize::put_expr; -use crate::ix::mutual::MutCtx; use crate::lean::{LeanIxBlockCompareDetail, LeanIxBlockCompareResult}; +use ix_common::env::Name; +use ix_compile::compile::{ + BlockCache, CompileState, compile_env, compile_expr, +}; +use ix_compile::mutual::MutCtx; +use ixon::serialize::put_expr; use lean_ffi::object::{LeanBorrowed, LeanByteArray, LeanList, LeanOwned}; -use crate::ffi::lean_env::{ +use crate::lean_env::{ Cache as LeanCache, GlobalCache, decode_expr, decode_name, }; @@ -195,7 +197,7 @@ pub unsafe extern "C" fn rs_free_compiled_env(ptr: *mut RustBlockEnv) { pub extern "C" fn rs_build_compiled_env( env_consts_ptr: LeanList>, ) -> *mut RustBlockEnv { - use crate::ffi::lean_env::decode_env; + use crate::lean_env::decode_env; // Decode Lean environment let rust_env = decode_env(env_consts_ptr); diff --git a/src/ffi/ixon/constant.rs b/crates/ffi/src/lean_ixon/constant.rs similarity index 97% rename from src/ffi/ixon/constant.rs rename to crates/ffi/src/lean_ixon/constant.rs index eabde98ba..d5fe112f1 100644 --- a/src/ffi/ixon/constant.rs +++ b/crates/ffi/src/lean_ixon/constant.rs @@ -6,14 +6,6 @@ use std::sync::Arc; -use crate::ix::ixon::constant::{ - Axiom as IxonAxiom, Constant as IxonConstant, - ConstantInfo as IxonConstantInfo, Constructor as IxonConstructor, - ConstructorProj, DefKind, Definition as IxonDefinition, DefinitionProj, - Inductive as IxonInductive, InductiveProj, MutConst, - Quotient as IxonQuotient, Recursor as IxonRecursor, RecursorProj, - RecursorRule as IxonRecursorRule, -}; use crate::lean::{ LeanIxAddress, LeanIxonAxiom, LeanIxonConstant, LeanIxonConstantInfo, LeanIxonConstructor, LeanIxonConstructorProj, LeanIxonDefinition, @@ -21,6 +13,14 @@ use crate::lean::{ LeanIxonInductiveProj, LeanIxonMutConst, LeanIxonQuotient, LeanIxonRecursor, LeanIxonRecursorProj, LeanIxonRecursorRule, LeanIxonUniv, }; +use ixon::constant::{ + Axiom as IxonAxiom, Constant as IxonConstant, + ConstantInfo as IxonConstantInfo, Constructor as IxonConstructor, + ConstructorProj, DefKind, Definition as IxonDefinition, DefinitionProj, + Inductive as IxonInductive, InductiveProj, MutConst, + Quotient as IxonQuotient, Recursor as IxonRecursor, RecursorProj, + RecursorRule as IxonRecursorRule, +}; #[cfg(feature = "test-ffi")] use lean_ffi::object::LeanBorrowed; use lean_ffi::object::{LeanArray, LeanOwned, LeanRef}; @@ -47,9 +47,9 @@ impl LeanIxonDefinition { }; ctor.set_num_8(0, kind_val); let safety_val: u8 = match def.safety { - crate::ix::env::DefinitionSafety::Unsafe => 0, - crate::ix::env::DefinitionSafety::Safe => 1, - crate::ix::env::DefinitionSafety::Partial => 2, + ix_common::env::DefinitionSafety::Unsafe => 0, + ix_common::env::DefinitionSafety::Safe => 1, + ix_common::env::DefinitionSafety::Partial => 2, }; ctor.set_num_8(1, safety_val); ctor @@ -75,9 +75,9 @@ impl LeanIxonDefinition { }; let safety_val = self.get_num_8(1); let safety = match safety_val { - 0 => crate::ix::env::DefinitionSafety::Unsafe, - 1 => crate::ix::env::DefinitionSafety::Safe, - 2 => crate::ix::env::DefinitionSafety::Partial, + 0 => ix_common::env::DefinitionSafety::Unsafe, + 1 => ix_common::env::DefinitionSafety::Safe, + 2 => ix_common::env::DefinitionSafety::Partial, _ => panic!("Invalid DefinitionSafety: {}", safety_val), }; IxonDefinition { kind, safety, lvls, typ, value } @@ -210,10 +210,10 @@ impl LeanIxonQuotient { ctor.set_obj(0, typ_obj); ctor.set_num_64(0, quot.lvls); let kind_val: u8 = match quot.kind { - crate::ix::env::QuotKind::Type => 0, - crate::ix::env::QuotKind::Ctor => 1, - crate::ix::env::QuotKind::Lift => 2, - crate::ix::env::QuotKind::Ind => 3, + ix_common::env::QuotKind::Type => 0, + ix_common::env::QuotKind::Ctor => 1, + ix_common::env::QuotKind::Lift => 2, + ix_common::env::QuotKind::Ind => 3, }; ctor.set_num_8(0, kind_val); ctor @@ -229,10 +229,10 @@ impl LeanIxonQuotient { let lvls = self.get_num_64(0); let kind_val = self.get_num_8(0); let kind = match kind_val { - 0 => crate::ix::env::QuotKind::Type, - 1 => crate::ix::env::QuotKind::Ctor, - 2 => crate::ix::env::QuotKind::Lift, - 3 => crate::ix::env::QuotKind::Ind, + 0 => ix_common::env::QuotKind::Type, + 1 => ix_common::env::QuotKind::Ctor, + 2 => ix_common::env::QuotKind::Lift, + 3 => ix_common::env::QuotKind::Ind, _ => panic!("Invalid QuotKind: {}", kind_val), }; IxonQuotient { kind, lvls, typ } diff --git a/src/ffi/ixon/enums.rs b/crates/ffi/src/lean_ixon/enums.rs similarity index 97% rename from src/ffi/ixon/enums.rs rename to crates/ffi/src/lean_ixon/enums.rs index 0afa0593e..54fa7a4fd 100644 --- a/src/ffi/ixon/enums.rs +++ b/crates/ffi/src/lean_ixon/enums.rs @@ -1,10 +1,10 @@ //! Ixon enum types: DefKind, DefinitionSafety, QuotKind build/decode/roundtrip FFI. -use crate::ix::env::{DefinitionSafety, QuotKind}; -use crate::ix::ixon::constant::DefKind; use crate::lean::{ LeanIxonDefKind, LeanIxonDefinitionSafety, LeanIxonQuotKind, }; +use ix_common::env::{DefinitionSafety, QuotKind}; +use ixon::constant::DefKind; #[cfg(feature = "test-ffi")] use lean_ffi::object::LeanBorrowed; use lean_ffi::object::{LeanOwned, LeanRef}; diff --git a/src/ffi/ixon/env.rs b/crates/ffi/src/lean_ixon/env.rs similarity index 97% rename from src/ffi/ixon/env.rs rename to crates/ffi/src/lean_ixon/env.rs index 51a80c5fa..398ada44e 100644 --- a/src/ffi/ixon/env.rs +++ b/crates/ffi/src/lean_ixon/env.rs @@ -3,24 +3,24 @@ //! Provides full decode/build cycle for RawEnv and its component types: //! RawConst, RawNamed, RawBlob, RawComm. -use crate::ix::address::Address; -use crate::ix::env::{Name, ReducibilityHints}; -use crate::ix::ixon::comm::Comm; -use crate::ix::ixon::constant::Constant as IxonConstant; -use crate::ix::ixon::env::{Env as IxonEnv, LazyIndex, Named as IxonNamed}; -use crate::ix::ixon::merkle::merkle_root_canonical; -use crate::ix::ixon::metadata::ConstantMeta; use crate::lean::{ LeanIxName, LeanIxonComm, LeanIxonConstant, LeanIxonConstantMeta, LeanIxonRawBlob, LeanIxonRawComm, LeanIxonRawConst, LeanIxonRawConstSlice, LeanIxonRawEnv, LeanIxonRawEnvLazy, LeanIxonRawNameEntry, LeanIxonRawNamed, LeanIxonRawNamedLite, }; +use ix_common::address::Address; +use ix_common::env::{Name, ReducibilityHints}; +use ixon::comm::Comm; +use ixon::constant::Constant as IxonConstant; +use ixon::env::{Env as IxonEnv, LazyIndex, Named as IxonNamed}; +use ixon::merkle::merkle_root_canonical; +use ixon::metadata::ConstantMeta; use lean_ffi::object::{ LeanArray, LeanBorrowed, LeanByteArray, LeanExcept, LeanOwned, LeanRef, }; -use crate::ffi::builder::LeanBuildCache; +use crate::builder::LeanBuildCache; use crate::lean::LeanIxAddress; // ============================================================================= @@ -567,7 +567,7 @@ pub extern "C" fn rs_de_env_lazy( obj: LeanByteArray>, ) -> LeanExcept { let data = obj.as_bytes(); - match crate::ix::ixon::env::Env::parse_lazy_index(data) { + match IxonEnv::parse_lazy_index(data) { Ok(index) => LeanExcept::ok(build_raw_env_lazy(&index)), Err(e) => LeanExcept::error_string(&format!("rs_de_env_lazy: {e}")), } diff --git a/src/ffi/ixon/expr.rs b/crates/ffi/src/lean_ixon/expr.rs similarity index 99% rename from src/ffi/ixon/expr.rs rename to crates/ffi/src/lean_ixon/expr.rs index 4fb710913..cea241630 100644 --- a/src/ffi/ixon/expr.rs +++ b/crates/ffi/src/lean_ixon/expr.rs @@ -2,8 +2,8 @@ use std::sync::Arc; -use crate::ix::ixon::expr::Expr as IxonExpr; use crate::lean::LeanIxonExpr; +use ixon::expr::Expr as IxonExpr; use lean_ffi::object::{LeanArray, LeanBorrowed, LeanOwned, LeanRef}; /// Decode Array UInt64 from Lean. diff --git a/src/ffi/ixon/meta.rs b/crates/ffi/src/lean_ixon/meta.rs similarity index 99% rename from src/ffi/ixon/meta.rs rename to crates/ffi/src/lean_ixon/meta.rs index 3962f65d0..13db8ec0e 100644 --- a/src/ffi/ixon/meta.rs +++ b/crates/ffi/src/lean_ixon/meta.rs @@ -2,19 +2,19 @@ //! //! Includes: DataValue, KVMap, ExprMetaData, ExprMetaArena, ConstantMeta, Named, Comm -use crate::ix::address::Address; -use crate::ix::env::BinderInfo; -use crate::ix::ixon::Comm; -use crate::ix::ixon::env::Named; -use crate::ix::ixon::metadata::{ - ConstantMeta, ConstantMetaInfo, DataValue as IxonDataValue, ExprMeta, - ExprMetaData, KVMap, -}; use crate::lean::{ LeanIxReducibilityHints, LeanIxonComm, LeanIxonConstantMeta, LeanIxonDataValue, LeanIxonExprMetaArena, LeanIxonExprMetaData, LeanIxonNamed, }; +use ix_common::address::Address; +use ix_common::env::BinderInfo; +use ixon::Comm; +use ixon::env::Named; +use ixon::metadata::{ + ConstantMeta, ConstantMetaInfo, DataValue as IxonDataValue, ExprMeta, + ExprMetaData, KVMap, +}; use lean_ffi::object::{ LeanArray, LeanBorrowed, LeanOption, LeanOwned, LeanProd, LeanRef, }; diff --git a/src/ffi/ixon/serialize.rs b/crates/ffi/src/lean_ixon/serialize.rs similarity index 97% rename from src/ffi/ixon/serialize.rs rename to crates/ffi/src/lean_ixon/serialize.rs index 5106a9b63..68061c03a 100644 --- a/src/ffi/ixon/serialize.rs +++ b/crates/ffi/src/lean_ixon/serialize.rs @@ -5,13 +5,13 @@ use std::sync::Arc; -use crate::ix::address::Address; -use crate::ix::ixon::serialize::put_expr; -use crate::ix::ixon::sharing::hash_expr; -use crate::ix::ixon::univ::put_univ; use crate::lean::{ LeanIxAddress, LeanIxonConstant, LeanIxonExpr, LeanIxonRawEnv, LeanIxonUniv, }; +use ix_common::address::Address; +use ixon::serialize::put_expr; +use ixon::sharing::hash_expr; +use ixon::univ::put_univ; use lean_ffi::object::{LeanBorrowed, LeanByteArray, LeanOwned}; /// Check if Lean's computed hash matches Rust's computed hash. @@ -77,7 +77,7 @@ pub extern "C" fn rs_eq_env_serialization( raw_env_obj: LeanIxonRawEnv>, bytes_obj: LeanByteArray>, ) -> bool { - use crate::ix::ixon::env::Env; + use ixon::env::Env; let debug = std::env::var("IX_DEBUG_SERDE").is_ok(); let decoded = raw_env_obj.decode(); @@ -246,7 +246,7 @@ pub extern "C" fn rs_eq_env_serialization( extern "C" fn rs_env_serde_roundtrip( lean_bytes_obj: LeanByteArray, ) -> bool { - use crate::ix::ixon::env::Env; + use ixon::env::Env; // Get bytes from Lean ByteArray let lean_bytes = lean_bytes_obj.as_bytes().to_vec(); @@ -294,7 +294,7 @@ extern "C" fn rs_env_serde_roundtrip( extern "C" fn rs_env_serde_check( lean_bytes_obj: LeanByteArray, ) -> bool { - use crate::ix::ixon::env::Env; + use ixon::env::Env; // Get bytes from Lean ByteArray let lean_bytes = lean_bytes_obj.as_bytes().to_vec(); diff --git a/src/ffi/ixon/sharing.rs b/crates/ffi/src/lean_ixon/sharing.rs similarity index 95% rename from src/ffi/ixon/sharing.rs rename to crates/ffi/src/lean_ixon/sharing.rs index 850650813..9811fb63d 100644 --- a/src/ffi/ixon/sharing.rs +++ b/crates/ffi/src/lean_ixon/sharing.rs @@ -2,12 +2,10 @@ use std::sync::Arc; -use crate::ix::ixon::expr::Expr as IxonExpr; -use crate::ix::ixon::serialize::put_expr; -use crate::ix::ixon::sharing::{ - analyze_block, build_sharing_vec, decide_sharing, -}; use crate::lean::LeanIxonExpr; +use ixon::expr::Expr as IxonExpr; +use ixon::serialize::put_expr; +use ixon::sharing::{analyze_block, build_sharing_vec, decide_sharing}; use lean_ffi::object::{LeanArray, LeanBorrowed, LeanByteArray, LeanOwned}; /// FFI: Debug sharing analysis - print usage counts for subterms with usage >= 2. @@ -23,7 +21,7 @@ pub extern "C" fn rs_debug_sharing_analysis( let (info_map, _ptr_to_hash, topo_order) = analyze_block(&exprs, false); let effective_sizes = - crate::ix::ixon::sharing::compute_effective_sizes(&info_map, &topo_order); + ixon::sharing::compute_effective_sizes(&info_map, &topo_order); println!("[Rust] Found {} unique subterms", info_map.len()); diff --git a/src/ffi/ixon/univ.rs b/crates/ffi/src/lean_ixon/univ.rs similarity index 98% rename from src/ffi/ixon/univ.rs rename to crates/ffi/src/lean_ixon/univ.rs index c85dde83f..57b8eab12 100644 --- a/src/ffi/ixon/univ.rs +++ b/crates/ffi/src/lean_ixon/univ.rs @@ -2,8 +2,8 @@ use std::sync::Arc; -use crate::ix::ixon::univ::Univ; use crate::lean::LeanIxonUniv; +use ixon::univ::Univ; use lean_ffi::object::{LeanArray, LeanBorrowed, LeanOwned, LeanRef}; impl LeanIxonUniv { diff --git a/crates/ffi/src/lib.rs b/crates/ffi/src/lib.rs new file mode 100644 index 000000000..38df7e1dd --- /dev/null +++ b/crates/ffi/src/lib.rs @@ -0,0 +1,60 @@ +// `_iroh` provides dummy fallbacks when the `net` feature is disabled or on +// `aarch64-darwin` (where iroh doesn't work). Lean and C don't support feature +// flags, so we always expose iroh symbols — they just panic with an error +// message when called without `net`. +#[cfg(any( + not(feature = "net"), + all(target_os = "macos", target_arch = "aarch64") +))] +pub mod _iroh; +pub mod aiur; +pub mod byte_array; +#[cfg(all( + feature = "net", + not(all(target_os = "macos", target_arch = "aarch64")) +))] +pub mod iroh; +pub mod keccak; +pub mod lean_env; +#[cfg(all( + feature = "net", + not(all(target_os = "macos", target_arch = "aarch64")) +))] +pub mod lean_iroh; +pub mod texray; +pub mod unsigned; + +pub mod builder; +pub mod compile; +pub mod graph; +pub mod ix; +pub mod kernel; +pub mod lean_ixon; +pub mod primitives; +#[cfg(feature = "test-ffi")] +pub mod refcount; + +pub mod lean; +mod sha256; + +#[global_allocator] +static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; + +#[cfg(feature = "test-ffi")] +use lean_ffi::object::{ + LeanArray, LeanBorrowed, LeanByteArray, LeanOwned, LeanRef, +}; + +#[cfg(feature = "test-ffi")] +#[unsafe(no_mangle)] +extern "C" fn rs_boxed_u32s_are_equivalent_to_bytes( + u32s: LeanArray, + bytes: LeanByteArray>, +) -> bool { + let u32s_flat: Vec = u32s + .map(|elem| elem.unbox_u32()) + .into_iter() + .flat_map(u32::to_le_bytes) + .collect(); + u32s_flat == bytes.as_bytes() +} diff --git a/src/ffi/primitives.rs b/crates/ffi/src/primitives.rs similarity index 91% rename from src/ffi/primitives.rs rename to crates/ffi/src/primitives.rs index c73e1448d..894081890 100644 --- a/src/ffi/primitives.rs +++ b/crates/ffi/src/primitives.rs @@ -4,6 +4,7 @@ //! (ix-specific types not covered by the lean-ffi test suite). use lean_ffi::nat::Nat; +use lean_ffi::object::LeanNat; #[cfg(feature = "test-ffi")] use lean_ffi::object::{LeanArray, LeanCtor, LeanOwned}; use lean_ffi::object::{LeanBorrowed, LeanRef}; @@ -34,8 +35,8 @@ fn build_assoc_list_nat_nat(pairs: &[(Nat, Nat)]) -> LeanOwned { let mut list = LeanOwned::box_usize(0); // nil for (k, v) in pairs.iter().rev() { let cons = LeanCtor::alloc(1, 3, 0); // AssocList.cons - cons.set(0, Nat::to_lean(k)); - cons.set(1, Nat::to_lean(v)); + cons.set(0, LeanNat::from_nat(k)); + cons.set(1, LeanNat::from_nat(v)); cons.set(2, list); list = cons.into(); } @@ -53,7 +54,7 @@ pub extern "C" fn rs_roundtrip_dhashmap_raw_nat_nat( } let raw_ctor = raw_ptr.as_ctor(); - let size = Nat::from_obj(&raw_ctor.get(0)); + let size = LeanNat::to_nat(&raw_ctor.get(0)); let buckets = raw_ctor.get(1).as_array(); // Decode and rebuild buckets @@ -85,15 +86,15 @@ pub extern "C" fn rs_roundtrip_dhashmap_raw_nat_nat( let old_bucket = new_buckets.get(bucket_idx).to_owned_ref(); let new_bucket = LeanCtor::alloc(1, 3, 0); - new_bucket.set(0, Nat::to_lean(k)); - new_bucket.set(1, Nat::to_lean(v)); + new_bucket.set(0, LeanNat::from_nat(k)); + new_bucket.set(1, LeanNat::from_nat(v)); new_bucket.set(2, old_bucket); new_buckets.set(bucket_idx, new_bucket); } // Build Raw let raw = LeanCtor::alloc(0, 2, 0); - raw.set(0, Nat::to_lean(&size)); + raw.set(0, LeanNat::from_nat(&size)); raw.set(1, new_buckets); raw.into() } @@ -119,7 +120,7 @@ pub extern "C" fn rs_roundtrip_hashmap_nat_nat( map_ptr: LeanCtor>, ) -> LeanOwned { // Due to unboxing, map_ptr points directly to Raw - let size = Nat::from_obj(&map_ptr.get(0)); + let size = LeanNat::to_nat(&map_ptr.get(0)); let buckets = map_ptr.get(1).as_array(); // Decode buckets (Array of AssocLists) @@ -160,8 +161,8 @@ pub extern "C" fn rs_roundtrip_hashmap_nat_nat( // Build AssocList.cons key value tail (tag 1, 3 fields) let new_bucket = LeanCtor::alloc(1, 3, 0); - new_bucket.set(0, Nat::to_lean(k)); - new_bucket.set(1, Nat::to_lean(v)); + new_bucket.set(0, LeanNat::from_nat(k)); + new_bucket.set(1, LeanNat::from_nat(v)); new_bucket.set(2, old_bucket); new_buckets.set(bucket_idx, new_bucket); } @@ -169,7 +170,7 @@ pub extern "C" fn rs_roundtrip_hashmap_nat_nat( // Build Raw (ctor 0, 2 fields: size, buckets) // Due to unboxing, this IS the HashMap let raw = LeanCtor::alloc(0, 2, 0); - raw.set(0, Nat::to_lean(&size)); + raw.set(0, LeanNat::from_nat(&size)); raw.set(1, new_buckets); raw.into() } @@ -188,7 +189,7 @@ pub fn decode_assoc_list_nat_nat(obj: LeanBorrowed<'_>) -> Vec<(Nat, Nat)> { let key = ctor.get(0); let val = ctor.get(1); let next = ctor.get(2).as_raw(); - result.push((Nat::from_obj(&key), Nat::from_obj(&val))); + result.push((LeanNat::to_nat(&key), LeanNat::to_nat(&val))); current = unsafe { LeanBorrowed::from_raw(next) }; } diff --git a/src/ffi/refcount.rs b/crates/ffi/src/refcount.rs similarity index 96% rename from src/ffi/refcount.rs rename to crates/ffi/src/refcount.rs index f5edd77c6..a68345991 100644 --- a/src/ffi/refcount.rs +++ b/crates/ffi/src/refcount.rs @@ -17,11 +17,12 @@ use std::thread; use lean_ffi::LeanShared; use lean_ffi::nat::Nat; +use lean_ffi::object::LeanNat; use lean_ffi::object::{ LeanArray, LeanBorrowed, LeanList, LeanOwned, LeanRef, LeanString, }; -use crate::ffi::builder::LeanBuildCache; +use crate::builder::LeanBuildCache; use crate::lean::{LeanIxExpr, LeanIxLevel, LeanIxName}; // ============================================================================= @@ -92,7 +93,7 @@ pub extern "C" fn rs_refcount_deep_borrow_expr( _ => 1, } } - Nat::from(count_nodes(&expr)).to_lean().into() + LeanNat::from_nat(&Nat::from(count_nodes(&expr))).into() } // ============================================================================= @@ -109,7 +110,7 @@ pub extern "C" fn rs_refcount_owned_array_drop( for i in 0..count { let _ = LeanIxName(arr.get(i)).decode(); } - Nat::from(count as u64).to_lean().into() + LeanNat::from_nat(&Nat::from(count as u64)).into() } /// Take an owned List of Exprs, consume and count. @@ -122,7 +123,7 @@ pub extern "C" fn rs_refcount_owned_list_drop( let _ = LeanIxExpr(elem).decode(); count += 1; } - Nat::from(count).to_lean().into() + LeanNat::from_nat(&Nat::from(count)).into() } // ============================================================================= @@ -140,7 +141,8 @@ pub extern "C" fn rs_refcount_clone_and_compare( let decoded1 = owned1.decode(); let decoded2 = owned2.decode(); // owned1 and owned2 both dropped → two lean_dec calls - Nat::from(if decoded1 == decoded2 { 1u64 } else { 0u64 }).to_lean().into() + LeanNat::from_nat(&Nat::from(if decoded1 == decoded2 { 1u64 } else { 0u64 })) + .into() } // ============================================================================= @@ -180,7 +182,7 @@ pub extern "C" fn rs_refcount_nested_borrow( let _level = LeanIxLevel(pair.get(1)).decode(); count += 1; } - Nat::from(count).to_lean().into() + LeanNat::from_nat(&Nat::from(count)).into() } // ============================================================================= @@ -313,7 +315,7 @@ pub extern "C" fn rs_mt_parallel_decode_names( .collect(); let total: u64 = handles.into_iter().map(|h| h.join().unwrap()).sum(); - Nat::from(total).to_lean().into() + LeanNat::from_nat(&Nat::from(total)).into() } /// Mark an array of Ix.Exprs as MT, decode in parallel from N threads. @@ -352,7 +354,7 @@ pub extern "C" fn rs_mt_parallel_decode_exprs( .collect(); let total: u64 = handles.into_iter().map(|h| h.join().unwrap()).sum(); - Nat::from(total).to_lean().into() + LeanNat::from_nat(&Nat::from(total)).into() } /// Parallel roundtrip: mark array of Names as MT, each thread decodes @@ -364,7 +366,7 @@ pub extern "C" fn rs_mt_parallel_roundtrip_names( arr: LeanArray>, n_threads: usize, ) -> LeanArray { - use crate::ix::env::Name; + use ix_common::env::Name; let shared = LeanShared::new(arr.inner().to_owned_ref()); let len = arr.len(); @@ -428,5 +430,5 @@ pub extern "C" fn rs_mt_shared_expr_stress( .collect(); let total: u64 = handles.into_iter().map(|h| h.join().unwrap()).sum(); - Nat::from(total).to_lean().into() + LeanNat::from_nat(&Nat::from(total)).into() } diff --git a/src/sha256.rs b/crates/ffi/src/sha256.rs similarity index 100% rename from src/sha256.rs rename to crates/ffi/src/sha256.rs diff --git a/src/ffi/texray.rs b/crates/ffi/src/texray.rs similarity index 100% rename from src/ffi/texray.rs rename to crates/ffi/src/texray.rs diff --git a/src/ffi/unsigned.rs b/crates/ffi/src/unsigned.rs similarity index 100% rename from src/ffi/unsigned.rs rename to crates/ffi/src/unsigned.rs diff --git a/crates/ixon/Cargo.toml b/crates/ixon/Cargo.toml new file mode 100644 index 000000000..d2c0183c4 --- /dev/null +++ b/crates/ixon/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "ixon" +version.workspace = true +edition.workspace = true +license.workspace = true + +[dependencies] +bignat = { workspace = true } +blake3 = { workspace = true } +dashmap = { workspace = true } +indexmap = { workspace = true } +ix-common = { workspace = true } +memmap2 = { workspace = true } +num-bigint = { workspace = true } +rustc-hash = { workspace = true } +sha2 = { workspace = true } +tiny-keccak = { workspace = true } + +[target.'cfg(not(target_arch = "riscv64"))'.dependencies] +dashmap = { workspace = true, features = ["rayon"] } +rayon = { workspace = true } + +[dev-dependencies] +ix-common = { workspace = true, features = ["quickcheck"] } +quickcheck = { workspace = true } +quickcheck_macros = { workspace = true } + +[lints] +workspace = true diff --git a/src/ix/ixon/assumption_tree.rs b/crates/ixon/src/assumption_tree.rs similarity index 99% rename from src/ix/ixon/assumption_tree.rs rename to crates/ixon/src/assumption_tree.rs index 4cf61d2b9..05500c2f4 100644 --- a/src/ix/ixon/assumption_tree.rs +++ b/crates/ixon/src/assumption_tree.rs @@ -36,7 +36,7 @@ //! `leaves()` clean (it returns only real leaves, not the synthetic //! padding addresses). -use crate::ix::address::Address; +use ix_common::address::Address; use super::merkle::{MerklePath, leaf_hash, node_hash, zero_address}; use super::proof::{FLAG_CLAIM, VARIANT_ASSUMPTION_TREE}; diff --git a/src/ix/ixon/comm.rs b/crates/ixon/src/comm.rs similarity index 98% rename from src/ix/ixon/comm.rs rename to crates/ixon/src/comm.rs index a27afcb24..9de866ca9 100644 --- a/src/ix/ixon/comm.rs +++ b/crates/ixon/src/comm.rs @@ -3,7 +3,7 @@ #![allow(clippy::map_err_ignore)] #![allow(clippy::needless_pass_by_value)] -use crate::ix::address::Address; +use ix_common::address::Address; use super::tag::Tag4; @@ -81,6 +81,7 @@ impl Comm { mod tests { use super::*; use quickcheck::Arbitrary; + use quickcheck_macros::quickcheck; impl Arbitrary for Comm { fn arbitrary(g: &mut quickcheck::Gen) -> Self { diff --git a/src/ix/ixon/constant.rs b/crates/ixon/src/constant.rs similarity index 94% rename from src/ix/ixon/constant.rs rename to crates/ixon/src/constant.rs index 0f3115661..12cc881b2 100644 --- a/src/ix/ixon/constant.rs +++ b/crates/ixon/src/constant.rs @@ -10,8 +10,8 @@ use std::sync::Arc; -use crate::ix::address::Address; -use crate::ix::env::{DefinitionSafety, QuotKind}; +use ix_common::address::Address; +use ix_common::env::{DefinitionSafety, QuotKind}; use super::expr::Expr; use super::univ::Univ; @@ -310,10 +310,11 @@ pub fn ctor_proj_constant(idx: u64, cidx: u64, block: Address) -> Constant { #[cfg(test)] pub mod tests { use super::*; - use crate::ix::env::{DefinitionSafety, QuotKind}; - use crate::ix::ixon::expr::tests::arbitrary_expr; - use crate::ix::ixon::tests::gen_range; + use crate::expr::tests::arbitrary_expr; + use crate::tests::gen_range; + use ix_common::env::{DefinitionSafety, QuotKind}; use quickcheck::{Arbitrary, Gen}; + use quickcheck_macros::quickcheck; impl Arbitrary for DefKind { fn arbitrary(g: &mut Gen) -> Self { @@ -325,27 +326,6 @@ pub mod tests { } } - impl Arbitrary for DefinitionSafety { - fn arbitrary(g: &mut Gen) -> Self { - match u8::arbitrary(g) % 3 { - 0 => DefinitionSafety::Unsafe, - 1 => DefinitionSafety::Safe, - _ => DefinitionSafety::Partial, - } - } - } - - impl Arbitrary for QuotKind { - fn arbitrary(g: &mut Gen) -> Self { - match u8::arbitrary(g) % 4 { - 0 => QuotKind::Type, - 1 => QuotKind::Ctor, - 2 => QuotKind::Lift, - _ => QuotKind::Ind, - } - } - } - pub fn gen_sharing(g: &mut Gen) -> Vec> { (0..gen_range(g, 0..4)).map(|_| arbitrary_expr(g)).collect() } @@ -355,7 +335,7 @@ pub mod tests { } pub fn gen_univs(g: &mut Gen) -> Vec> { - use crate::ix::ixon::univ::tests::arbitrary_univ; + use crate::univ::tests::arbitrary_univ; (0..gen_range(g, 0..4)).map(|_| arbitrary_univ(g)).collect() } diff --git a/src/ix/ixon/env.rs b/crates/ixon/src/env.rs similarity index 89% rename from src/ix/ixon/env.rs rename to crates/ixon/src/env.rs index 060b25038..425d9b976 100644 --- a/src/ix/ixon/env.rs +++ b/crates/ixon/src/env.rs @@ -1,17 +1,17 @@ //! Environment for storing Ixon data. -use dashmap::DashMap; use rustc_hash::{FxHashMap, FxHashSet}; use std::collections::VecDeque; use std::sync::Arc; -use crate::ix::address::Address; -use crate::ix::env::{Name, ReducibilityHints}; +use ix_common::address::Address; +use ix_common::env::{Name, ReducibilityHints}; use super::comm::Comm; use super::constant::Constant; use super::lazy::LazyConstant; -use super::metadata::ConstantMeta; +use super::map::IxonMap; +use super::metadata::{ConstantMeta, ConstantMetaInfo}; /// A named constant with metadata. #[derive(Clone, Debug)] @@ -43,7 +43,7 @@ impl Named { /// correctly permute source-order aux motives/minors into canonical /// positions. Both arrays have one entry per source-walk-discovered aux. /// -/// This lives in `ixon::env` (not `compile::surgery`, where it originated) +/// This lives in `ixon::env` (not `ix_compile::surgery`, where it originated) /// so it can be persisted into the serialized Ixon environment as a /// side-table on [`Env::aux_layouts`]. The surgery layer re-exports it. /// @@ -108,15 +108,15 @@ pub struct LazyIndex { pub struct Env { /// Alpha-invariant constants: Address -> LazyConstant (raw bytes + /// optional materialized cache; see [`LazyConstant`]). - pub consts: DashMap, + pub consts: IxonMap, /// Named references: Name -> (constant address, metadata, ctx) - pub named: DashMap, + pub named: IxonMap, /// Raw data blobs: Address -> bytes - pub blobs: DashMap>, + pub blobs: IxonMap>, /// Hash-consed Lean.Name components: Address -> Name - pub names: DashMap, + pub names: IxonMap, /// Cryptographic commitments: commitment Address -> Comm - pub comms: DashMap, + pub comms: IxonMap, /// Reducibility hints sidecar harvested by [`Env::get_anon`] from the /// otherwise-discarded Named section. Keyed by the constant's /// projection/standalone address (i.e. `Named.addr` — the address the @@ -136,16 +136,22 @@ pub struct Env { impl Env { pub fn new() -> Self { Env { - consts: DashMap::new(), - named: DashMap::new(), - blobs: DashMap::new(), - names: DashMap::new(), - comms: DashMap::new(), + consts: IxonMap::new(), + named: IxonMap::new(), + blobs: IxonMap::new(), + names: IxonMap::new(), + comms: IxonMap::new(), anon_hints: FxHashMap::default(), } } /// Store a blob and return its content address. + /// + /// Host-only because `IxonMap` is `DashMap` here (interior-mutable `&self` + /// inserts). On `riscv64` `IxonMap` is `FxHashMap`, which requires `&mut`; + /// the guest builds `Env` via `Env::get` deserialization and doesn't need + /// the store helpers. + #[cfg(not(target_arch = "riscv64"))] pub fn store_blob(&self, bytes: Vec) -> Address { let addr = Address::hash(&bytes); self.blobs.insert(addr.clone(), bytes); @@ -162,12 +168,17 @@ impl Env { /// Serializes the constant once and pre-populates the /// [`LazyConstant`] cache so subsequent `Env::put` is a memcpy and /// the first `get_const` call is free. + /// + /// Host-only — see `store_blob`. + #[cfg(not(target_arch = "riscv64"))] pub fn store_const(&self, addr: Address, constant: Constant) { self.consts.insert(addr, LazyConstant::from_constant(constant)); } /// Store an already-serialized constant under `addr` (lazy load path). /// `bytes` must be exactly what `Constant::put` produced for `addr`. + /// Host-only — see `store_blob`. + #[cfg(not(target_arch = "riscv64"))] pub fn store_const_lazy(&self, addr: Address, bytes: Arc<[u8]>) { self.consts.insert(addr, LazyConstant::from_bytes(bytes)); } @@ -176,6 +187,8 @@ impl Env { /// `(mmap, offset, len)` must reference exactly what `Constant::put` /// produced for `addr`. Used by [`Env::get_anon_mmap`] to avoid /// heap-copying on-disk bytes — the OS page cache backs the slice. + /// Host-only — see `store_blob`. + #[cfg(not(target_arch = "riscv64"))] pub fn store_const_lazy_mmap( &self, addr: Address, @@ -208,7 +221,8 @@ impl Env { self.consts.get(addr).map(|r| Arc::from(r.value().raw_bytes())) } - /// Register a named constant. + /// Register a named constant. Host-only — see `store_blob`. + #[cfg(not(target_arch = "riscv64"))] pub fn register_name(&self, name: Name, named: Named) { self.named.insert(name, named); } @@ -218,7 +232,8 @@ impl Env { self.named.get(name).map(|r| r.clone()) } - /// Store a hash-consed name component. + /// Store a hash-consed name component. Host-only — see `store_blob`. + #[cfg(not(target_arch = "riscv64"))] pub fn store_name(&self, addr: Address, name: Name) { self.names.insert(addr, name); } @@ -228,7 +243,8 @@ impl Env { self.names.get(addr).map(|r| r.clone()) } - /// Store a commitment. + /// Store a commitment. Host-only — see `store_blob`. + #[cfg(not(target_arch = "riscv64"))] pub fn store_comm(&self, addr: Address, comm: Comm) { self.comms.insert(addr, comm); } @@ -248,6 +264,20 @@ impl Env { self.named.len() } + /// Addresses of the named constants the kernel typechecker would + /// iterate via `check_const` — every entry in `env.named` minus the + /// `Muts` mutual-block pointers (which aren't standalone checkables). + /// Matches `crates/ffi/src/kernel.rs::all_checkable_ixon_names` but + /// returns addresses rather than names and skips the sort. + pub fn checkable_addrs(&self) -> Vec
{ + self + .named + .iter() + .filter(|e| !matches!(e.value().meta.info, ConstantMetaInfo::Muts { .. })) + .map(|e| e.value().addr.clone()) + .collect() + } + /// Number of hash-consed name components. pub fn name_count(&self) -> usize { self.names.len() @@ -309,28 +339,31 @@ impl Env { } impl Clone for Env { + // `mut` is only needed on `riscv64` where `IxonMap` wraps `FxHashMap` and + // `insert` takes `&mut self`; on host `DashMap::insert` takes `&self`. + #[cfg_attr(not(target_arch = "riscv64"), allow(unused_mut))] fn clone(&self) -> Self { - let consts = DashMap::new(); + let mut consts = IxonMap::new(); for entry in self.consts.iter() { consts.insert(entry.key().clone(), entry.value().clone()); } - let named = DashMap::new(); + let mut named = IxonMap::new(); for entry in self.named.iter() { named.insert(entry.key().clone(), entry.value().clone()); } - let blobs = DashMap::new(); + let mut blobs = IxonMap::new(); for entry in self.blobs.iter() { blobs.insert(entry.key().clone(), entry.value().clone()); } - let names = DashMap::new(); + let mut names = IxonMap::new(); for entry in self.names.iter() { names.insert(entry.key().clone(), entry.value().clone()); } - let comms = DashMap::new(); + let mut comms = IxonMap::new(); for entry in self.comms.iter() { comms.insert(entry.key().clone(), entry.value().clone()); } @@ -349,9 +382,9 @@ impl Clone for Env { #[cfg(test)] mod tests { use super::*; - use crate::ix::env::Name; - use crate::ix::ixon::constant::{Axiom, Constant, ConstantInfo}; - use crate::ix::ixon::expr::Expr; + use crate::constant::{Axiom, Constant, ConstantInfo}; + use crate::expr::Expr; + use ix_common::env::Name; use std::sync::Arc; fn n(s: &str) -> Name { diff --git a/src/ix/ixon/error.rs b/crates/ixon/src/error.rs similarity index 99% rename from src/ix/ixon/error.rs rename to crates/ixon/src/error.rs index 26f22334a..0e2c84626 100644 --- a/src/ix/ixon/error.rs +++ b/crates/ixon/src/error.rs @@ -1,6 +1,6 @@ //! Custom error types for Ixon serialization and compilation. -use crate::ix::address::Address; +use ix_common::address::Address; /// Errors during serialization/deserialization. #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/src/ix/ixon/expr.rs b/crates/ixon/src/expr.rs similarity index 98% rename from src/ix/ixon/expr.rs rename to crates/ixon/src/expr.rs index 5a6f6267e..815ed69c9 100644 --- a/src/ix/ixon/expr.rs +++ b/crates/ixon/src/expr.rs @@ -145,10 +145,11 @@ impl Expr { #[cfg(test)] pub mod tests { use super::*; - use crate::ix::ixon::constant::Constant; - use crate::ix::ixon::serialize::{get_expr, put_expr}; - use crate::ix::ixon::tests::gen_range; + use crate::constant::Constant; + use crate::serialize::{get_expr, put_expr}; + use crate::tests::gen_range; use quickcheck::{Arbitrary, Gen}; + use quickcheck_macros::quickcheck; use std::ptr; #[derive(Clone, Copy)] @@ -169,7 +170,7 @@ pub mod tests { /// Generate an arbitrary Expr using pointer-tree technique (no stack overflow) pub fn arbitrary_expr(g: &mut Gen) -> Arc { - use crate::ix::ixon::tests::next_case; + use crate::tests::next_case; let mut root = Expr::Var(0); let mut stack = vec![&mut root as *mut Expr]; diff --git a/src/ix/ixon/lazy.rs b/crates/ixon/src/lazy.rs similarity index 96% rename from src/ix/ixon/lazy.rs rename to crates/ixon/src/lazy.rs index 269a82eb2..f08330e1a 100644 --- a/src/ix/ixon/lazy.rs +++ b/crates/ixon/src/lazy.rs @@ -45,7 +45,7 @@ use std::sync::Arc; use memmap2::Mmap; -use crate::ix::address::Address; +use ix_common::address::Address; use super::constant::{Constant, ConstantInfo}; @@ -246,9 +246,9 @@ impl Eq for LazyConstant {} #[cfg(test)] mod tests { use super::*; - use crate::ix::env::DefinitionSafety; - use crate::ix::ixon::constant::{Axiom, ConstantInfo, DefKind, Definition}; - use crate::ix::ixon::expr::Expr; + use crate::constant::{Axiom, ConstantInfo, DefKind, Definition}; + use crate::expr::Expr; + use ix_common::env::DefinitionSafety; fn axiom_constant() -> Constant { Constant::new(ConstantInfo::Axio(Axiom { @@ -391,7 +391,7 @@ mod tests { /// roundtrip it through `Constant::put`/`Constant::get` for the /// `peek_variant` Muts test. fn muts_constant() -> Constant { - use crate::ix::ixon::constant::{ConstantInfo, MutConst}; + use crate::constant::{ConstantInfo, MutConst}; let m1 = MutConst::Defn(Definition { kind: DefKind::Definition, safety: DefinitionSafety::Safe, @@ -431,8 +431,8 @@ mod tests { #[test] fn peek_variant_quot() { - use crate::ix::env::QuotKind; - use crate::ix::ixon::constant::{ConstantInfo, Quotient}; + use crate::constant::{ConstantInfo, Quotient}; + use ix_common::env::QuotKind; let q = Constant::new(ConstantInfo::Quot(Quotient { kind: QuotKind::Type, lvls: 1, @@ -443,7 +443,7 @@ mod tests { #[test] fn peek_variant_dprj() { - use crate::ix::ixon::constant::{ConstantInfo, DefinitionProj}; + use crate::constant::{ConstantInfo, DefinitionProj}; let p = Constant::new(ConstantInfo::DPrj(DefinitionProj { idx: 0, block: Address::hash(b"some-block"), @@ -453,7 +453,7 @@ mod tests { #[test] fn peek_variant_iprj() { - use crate::ix::ixon::constant::{ConstantInfo, InductiveProj}; + use crate::constant::{ConstantInfo, InductiveProj}; let p = Constant::new(ConstantInfo::IPrj(InductiveProj { idx: 0, block: Address::hash(b"some-block"), @@ -463,7 +463,7 @@ mod tests { #[test] fn peek_variant_rprj() { - use crate::ix::ixon::constant::{ConstantInfo, RecursorProj}; + use crate::constant::{ConstantInfo, RecursorProj}; let p = Constant::new(ConstantInfo::RPrj(RecursorProj { idx: 0, block: Address::hash(b"some-block"), @@ -473,7 +473,7 @@ mod tests { #[test] fn peek_variant_cprj() { - use crate::ix::ixon::constant::{ConstantInfo, ConstructorProj}; + use crate::constant::{ConstantInfo, ConstructorProj}; let p = Constant::new(ConstantInfo::CPrj(ConstructorProj { idx: 0, cidx: 0, diff --git a/src/ix/ixon.rs b/crates/ixon/src/lib.rs similarity index 98% rename from src/ix/ixon.rs rename to crates/ixon/src/lib.rs index 5c5405145..84a13addb 100644 --- a/src/ix/ixon.rs +++ b/crates/ixon/src/lib.rs @@ -13,6 +13,7 @@ pub mod env; pub mod error; pub mod expr; pub mod lazy; +pub mod map; pub mod merkle; pub mod metadata; pub mod proof; @@ -86,7 +87,7 @@ pub mod tests { #[cfg(test)] mod doc_examples { use super::*; - use crate::ix::address::Address; + use ix_common::address::Address; // ========================================================================= // Tag4 examples (docs section "Tag4 (4-bit flag)") @@ -373,8 +374,8 @@ mod doc_examples { fn definition_packed_kind_safety() { // DefKind::Definition = 0, DefinitionSafety::Safe = 1 // Packed: (0 << 2) | 1 = 0x01 - use crate::ix::env::DefinitionSafety; use constant::{DefKind, Definition}; + use ix_common::env::DefinitionSafety; let def = Definition { kind: DefKind::Definition, @@ -392,8 +393,8 @@ mod doc_examples { fn definition_opaque_unsafe() { // DefKind::Opaque = 1, DefinitionSafety::Unsafe = 0 // Packed: (1 << 2) | 0 = 0x04 - use crate::ix::env::DefinitionSafety; use constant::{DefKind, Definition}; + use ix_common::env::DefinitionSafety; let def = Definition { kind: DefKind::Opaque, @@ -411,8 +412,8 @@ mod doc_examples { fn definition_theorem_partial() { // DefKind::Theorem = 2, DefinitionSafety::Partial = 2 // Packed: (2 << 2) | 2 = 0x0A - use crate::ix::env::DefinitionSafety; use constant::{DefKind, Definition}; + use ix_common::env::DefinitionSafety; let def = Definition { kind: DefKind::Theorem, @@ -433,8 +434,8 @@ mod doc_examples { #[test] fn constant_defn_tag() { // Constant with Defn -> Tag4 { flag: 0xD, size: 0 } -> 0xD0 - use crate::ix::env::DefinitionSafety; use constant::{Constant, ConstantInfo, DefKind, Definition}; + use ix_common::env::DefinitionSafety; let constant = Constant::new(ConstantInfo::Defn(Definition { kind: DefKind::Definition, @@ -451,8 +452,8 @@ mod doc_examples { #[test] fn constant_muts_tag() { // Muts with 3 entries -> Tag4 { flag: 0xC, size: 3 } -> 0xC3 - use crate::ix::env::DefinitionSafety; use constant::{Constant, ConstantInfo, DefKind, Definition, MutConst}; + use ix_common::env::DefinitionSafety; let def = Definition { kind: DefKind::Definition, diff --git a/crates/ixon/src/map.rs b/crates/ixon/src/map.rs new file mode 100644 index 000000000..748ebafb1 --- /dev/null +++ b/crates/ixon/src/map.rs @@ -0,0 +1,101 @@ +//! Map type used by `Env`. +//! +//! On host, this is `dashmap::DashMap` — concurrent reads/writes for the +//! parallel ingress path. On `riscv64`, `DashMap` pulls in `parking_lot_core`, +//! whose `HashTable::new` calls `Instant::now()` (unsupported in the Zisk +//! guest's stdlib). Instead, alias to a thin single-threaded wrapper around +//! `rustc_hash::FxHashMap` that mirrors the subset of DashMap's API used here. + +#[cfg(not(target_arch = "riscv64"))] +pub use dashmap::DashMap as IxonMap; + +#[cfg(target_arch = "riscv64")] +pub use riscv_impl::IxonMap; + +#[cfg(target_arch = "riscv64")] +mod riscv_impl { + use std::collections::hash_map; + use std::hash::Hash; + use std::ops::Deref; + + use rustc_hash::FxHashMap; + + #[derive(Debug)] + pub struct IxonMap(FxHashMap); + + impl Default for IxonMap { + fn default() -> Self { + Self(FxHashMap::default()) + } + } + + impl IxonMap { + pub fn new() -> Self { + Self::default() + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn contains_key(&self, k: &K) -> bool { + self.0.contains_key(k) + } + + pub fn insert(&mut self, k: K, v: V) -> Option { + self.0.insert(k, v) + } + + pub fn clear(&mut self) { + self.0.clear(); + } + + pub fn get(&self, k: &K) -> Option> { + self.0.get_key_value(k).map(|(k, v)| MapRef { k, v }) + } + + pub fn get_mut(&mut self, k: &K) -> Option<&mut V> { + self.0.get_mut(k) + } + + pub fn iter(&self) -> Iter<'_, K, V> { + Iter(self.0.iter()) + } + } + + /// Read guard exposing DashMap's `key()` / `value()` shape. + pub struct MapRef<'a, K, V> { + k: &'a K, + v: &'a V, + } + + impl<'a, K, V> MapRef<'a, K, V> { + pub fn key(&self) -> &K { + self.k + } + + pub fn value(&self) -> &V { + self.v + } + } + + impl<'a, K, V> Deref for MapRef<'a, K, V> { + type Target = V; + fn deref(&self) -> &V { + self.v + } + } + + pub struct Iter<'a, K, V>(hash_map::Iter<'a, K, V>); + + impl<'a, K, V> Iterator for Iter<'a, K, V> { + type Item = MapRef<'a, K, V>; + fn next(&mut self) -> Option { + self.0.next().map(|(k, v)| MapRef { k, v }) + } + } +} diff --git a/src/ix/ixon/merkle.rs b/crates/ixon/src/merkle.rs similarity index 99% rename from src/ix/ixon/merkle.rs rename to crates/ixon/src/merkle.rs index 081f89e9d..78505f29a 100644 --- a/src/ix/ixon/merkle.rs +++ b/crates/ixon/src/merkle.rs @@ -30,7 +30,7 @@ //! introduces CVE-2012-2459-style malleability where two distinct leaf //! lists can produce the same root. -use crate::ix::address::Address; +use ix_common::address::Address; /// Domain-separation prefix for leaf hashes. pub const LEAF_DOMAIN: u8 = 0x00; @@ -197,6 +197,7 @@ pub fn verify_merkle_proof( mod tests { use super::*; use quickcheck::{Arbitrary, Gen}; + use quickcheck_macros::quickcheck; fn addr(seed: &[u8]) -> Address { Address::hash(seed) diff --git a/src/ix/ixon/metadata.rs b/crates/ixon/src/metadata.rs similarity index 96% rename from src/ix/ixon/metadata.rs rename to crates/ixon/src/metadata.rs index e6f48db10..c8f16d2c8 100644 --- a/src/ix/ixon/metadata.rs +++ b/crates/ixon/src/metadata.rs @@ -12,8 +12,8 @@ use std::collections::HashMap; use std::sync::Arc; -use crate::ix::address::Address; -use crate::ix::env::{self, BinderInfo, Name, ReducibilityHints}; +use ix_common::address::Address; +use ix_common::env::{self, BinderInfo, Name, ReducibilityHints}; use super::env::AuxLayout; use super::expr::Expr; @@ -330,7 +330,7 @@ pub fn resolve_kvmap( }, DataValue::OfNat(a) => { let bytes = ixon_env.get_blob(a)?; - env::DataValue::OfNat(lean_ffi::nat::Nat::from_le_bytes(&bytes)) + env::DataValue::OfNat(bignat::Nat::from_le_bytes(&bytes)) }, DataValue::OfInt(a) => { let bytes = ixon_env.get_blob(a)?; @@ -389,8 +389,8 @@ fn deser_addr(buf: &mut &[u8]) -> Option
{ fn deser_int(bytes: &[u8]) -> Option { let (&tag, rest) = bytes.split_first()?; match tag { - 0 => Some(env::Int::OfNat(lean_ffi::nat::Nat::from_le_bytes(rest))), - 1 => Some(env::Int::NegSucc(lean_ffi::nat::Nat::from_le_bytes(rest))), + 0 => Some(env::Int::OfNat(bignat::Nat::from_le_bytes(rest))), + 1 => Some(env::Int::NegSucc(bignat::Nat::from_le_bytes(rest))), _ => None, } } @@ -401,8 +401,8 @@ fn deser_substring( ) -> Option { let str_addr = deser_addr(buf)?; let s = String::from_utf8(ixon_env.get_blob(&str_addr)?).ok()?; - let start_pos = lean_ffi::nat::Nat::from(deser_tag0(buf)?); - let stop_pos = lean_ffi::nat::Nat::from(deser_tag0(buf)?); + let start_pos = bignat::Nat::from(deser_tag0(buf)?); + let stop_pos = bignat::Nat::from(deser_tag0(buf)?); Some(env::Substring { str: s, start_pos, stop_pos }) } @@ -413,9 +413,9 @@ fn deser_source_info( match deser_u8(buf)? { 0 => { let leading = deser_substring(buf, ixon_env)?; - let leading_pos = lean_ffi::nat::Nat::from(deser_tag0(buf)?); + let leading_pos = bignat::Nat::from(deser_tag0(buf)?); let trailing = deser_substring(buf, ixon_env)?; - let trailing_pos = lean_ffi::nat::Nat::from(deser_tag0(buf)?); + let trailing_pos = bignat::Nat::from(deser_tag0(buf)?); Some(env::SourceInfo::Original( leading, leading_pos, @@ -424,8 +424,8 @@ fn deser_source_info( )) }, 1 => { - let start = lean_ffi::nat::Nat::from(deser_tag0(buf)?); - let end = lean_ffi::nat::Nat::from(deser_tag0(buf)?); + let start = bignat::Nat::from(deser_tag0(buf)?); + let end = bignat::Nat::from(deser_tag0(buf)?); let canonical = deser_u8(buf)? != 0; Some(env::SourceInfo::Synthetic(start, end, canonical)) }, @@ -560,8 +560,15 @@ pub(super) fn get_vec_len(buf: &mut &[u8]) -> Result { // BinderInfo and ReducibilityHints serialization // =========================================================================== -impl BinderInfo { - pub fn put(&self, buf: &mut Vec) { +/// Extension trait for serializing/deserializing small env-side enums whose +/// types live in `ix-types` (so we can't define inherent impls here). +pub trait IxonByteSerde: Sized { + fn put_ser(&self, buf: &mut Vec); + fn get_ser(buf: &mut &[u8]) -> Result; +} + +impl IxonByteSerde for BinderInfo { + fn put_ser(&self, buf: &mut Vec) { match self { Self::Default => put_u8(0, buf), Self::Implicit => put_u8(1, buf), @@ -570,7 +577,7 @@ impl BinderInfo { } } - pub fn get_ser(buf: &mut &[u8]) -> Result { + fn get_ser(buf: &mut &[u8]) -> Result { match get_u8(buf)? { 0 => Ok(Self::Default), 1 => Ok(Self::Implicit), @@ -581,8 +588,8 @@ impl BinderInfo { } } -impl ReducibilityHints { - pub fn put(&self, buf: &mut Vec) { +impl IxonByteSerde for ReducibilityHints { + fn put_ser(&self, buf: &mut Vec) { match self { Self::Opaque => put_u8(0, buf), Self::Abbrev => put_u8(1, buf), @@ -593,7 +600,7 @@ impl ReducibilityHints { } } - pub fn get_ser(buf: &mut &[u8]) -> Result { + fn get_ser(buf: &mut &[u8]) -> Result { match get_u8(buf)? { 0 => Ok(Self::Opaque), 1 => Ok(Self::Abbrev), @@ -1004,7 +1011,7 @@ impl ConstantMetaInfo { put_u8(0, buf); put_idx(name, idx, buf)?; put_idx_vec(lvls, idx, buf)?; - hints.put(buf); + hints.put_ser(buf); put_idx_vec(all, idx, buf)?; put_idx_vec(ctx, idx, buf)?; arena.put_indexed(idx, buf)?; @@ -1182,28 +1189,6 @@ impl ConstantMetaInfo { #[cfg(test)] mod tests { use super::*; - use quickcheck::{Arbitrary, Gen}; - - impl Arbitrary for BinderInfo { - fn arbitrary(g: &mut Gen) -> Self { - match u8::arbitrary(g) % 4 { - 0 => Self::Default, - 1 => Self::Implicit, - 2 => Self::StrictImplicit, - _ => Self::InstImplicit, - } - } - } - - impl Arbitrary for ReducibilityHints { - fn arbitrary(g: &mut Gen) -> Self { - match u8::arbitrary(g) % 3 { - 0 => Self::Opaque, - 1 => Self::Abbrev, - _ => Self::Regular(u32::arbitrary(g)), - } - } - } #[test] fn test_binder_info_roundtrip() { @@ -1214,7 +1199,7 @@ mod tests { BinderInfo::InstImplicit, ] { let mut buf = Vec::new(); - bi.put(&mut buf); + bi.put_ser(&mut buf); assert_eq!(BinderInfo::get_ser(&mut buf.as_slice()).unwrap(), bi); } } @@ -1227,7 +1212,7 @@ mod tests { ReducibilityHints::Regular(42), ] { let mut buf = Vec::new(); - h.put(&mut buf); + h.put_ser(&mut buf); assert_eq!(ReducibilityHints::get_ser(&mut buf.as_slice()).unwrap(), h); } } diff --git a/src/ix/ixon/proof.rs b/crates/ixon/src/proof.rs similarity index 99% rename from src/ix/ixon/proof.rs rename to crates/ixon/src/proof.rs index 805f0017c..94fc2b6ec 100644 --- a/src/ix/ixon/proof.rs +++ b/crates/ixon/src/proof.rs @@ -8,8 +8,8 @@ //! order. This enables revealing specific fields of a committed constant without //! exposing the full structure. -use crate::ix::address::Address; -use crate::ix::env::{DefinitionSafety, QuotKind}; +use ix_common::address::Address; +use ix_common::env::{DefinitionSafety, QuotKind}; use super::constant::DefKind; use super::tag::{Tag0, Tag4}; @@ -144,7 +144,7 @@ pub enum RevealConstantInfo { /// Orthogonal to typechecking; carries no assumptions. /// - **Contains**: structural membership claim — `const_addr` is a leaf /// in the merkle tree rooted at `tree`. Used by the aggregation -/// circuit to discharge a leaf from a conditional claim's assumption +/// circuit to resolve a leaf from a conditional claim's assumption /// set. Carries no assumptions itself. /// /// The `assumptions` root may be any merkle tree (canonical sorted+ @@ -1176,6 +1176,7 @@ impl Proof { mod tests { use super::*; use quickcheck::{Arbitrary, Gen}; + use quickcheck_macros::quickcheck; // ========== Arbitrary impls ========== diff --git a/src/ix/ixon/serialize.rs b/crates/ixon/src/serialize.rs similarity index 94% rename from src/ix/ixon/serialize.rs rename to crates/ixon/src/serialize.rs index c6ca0ce1c..0acffb0fc 100644 --- a/src/ix/ixon/serialize.rs +++ b/crates/ixon/src/serialize.rs @@ -9,8 +9,8 @@ use std::sync::Arc; -use crate::ix::address::Address; -use crate::ix::env::{DefinitionSafety, QuotKind}; +use ix_common::address::Address; +use ix_common::env::{DefinitionSafety, QuotKind, ReducibilityHints}; use super::constant::{ Axiom, Constant, ConstantInfo, Constructor, ConstructorProj, DefKind, @@ -18,6 +18,7 @@ use super::constant::{ Recursor, RecursorProj, RecursorRule, }; use super::expr::Expr; +use super::metadata::IxonByteSerde; use super::tag::{Tag0, Tag4}; use super::univ::{Univ, get_univ, put_univ}; @@ -75,6 +76,27 @@ fn put_address(a: &Address, buf: &mut Vec) { put_bytes(a.as_bytes(), buf); } +/// Read the optional trailing `anon_hints` section written by `Env::put`. +/// No-op when no bytes remain after the comms section — backward-compatible +/// with `.ixe` that have no hints or predate the section. Inserts into +/// `env.anon_hints` (overwriting any value harvested from a Named section, +/// with the identical value, so the order of harvest vs. section is moot). +fn read_anon_hints_section( + buf: &mut &[u8], + env: &mut Env, +) -> Result<(), String> { + if buf.is_empty() { + return Ok(()); + } + let n = get_u64(buf)?; + for _ in 0..n { + let addr = get_address(buf)?; + let hints = ReducibilityHints::get_ser(buf)?; + env.anon_hints.insert(addr, hints); + } + Ok(()) +} + fn get_address(buf: &mut &[u8]) -> Result { if buf.len() < 32 { return Err(format!("get_address: need 32 bytes, have {}", buf.len())); @@ -425,28 +447,26 @@ impl DefKind { } } -impl DefinitionSafety { - fn to_u8(self) -> u8 { - match self { - Self::Unsafe => 0, - Self::Safe => 1, - Self::Partial => 2, - } +fn definition_safety_to_u8(s: DefinitionSafety) -> u8 { + match s { + DefinitionSafety::Unsafe => 0, + DefinitionSafety::Safe => 1, + DefinitionSafety::Partial => 2, } +} - fn from_u8(x: u8) -> Result { - match x { - 0 => Ok(Self::Unsafe), - 1 => Ok(Self::Safe), - 2 => Ok(Self::Partial), - x => Err(format!("DefinitionSafety::from_u8: invalid {x}")), - } +fn definition_safety_from_u8(x: u8) -> Result { + match x { + 0 => Ok(DefinitionSafety::Unsafe), + 1 => Ok(DefinitionSafety::Safe), + 2 => Ok(DefinitionSafety::Partial), + x => Err(format!("DefinitionSafety::from_u8: invalid {x}")), } } /// Pack DefKind (2 bits) and DefinitionSafety (2 bits) into a single byte. fn pack_def_kind_safety(kind: DefKind, safety: DefinitionSafety) -> u8 { - (kind.to_u8() << 2) | safety.to_u8() + (kind.to_u8() << 2) | definition_safety_to_u8(safety) } /// Unpack DefKind and DefinitionSafety from a single byte. @@ -454,12 +474,12 @@ fn unpack_def_kind_safety( b: u8, ) -> Result<(DefKind, DefinitionSafety), String> { let kind = DefKind::from_u8(b >> 2)?; - let safety = DefinitionSafety::from_u8(b & 0x3)?; + let safety = definition_safety_from_u8(b & 0x3)?; Ok((kind, safety)) } -impl QuotKind { - pub fn put_ser(&self, buf: &mut Vec) { +impl IxonByteSerde for QuotKind { + fn put_ser(&self, buf: &mut Vec) { match self { Self::Type => put_u8(0, buf), Self::Ctor => put_u8(1, buf), @@ -468,7 +488,7 @@ impl QuotKind { } } - pub fn get_ser(buf: &mut &[u8]) -> Result { + fn get_ser(buf: &mut &[u8]) -> Result { match get_u8(buf)? { 0 => Ok(Self::Type), 1 => Ok(Self::Ctor), @@ -870,8 +890,8 @@ impl Constant { // Name serialization // ============================================================================ -use crate::ix::env::{Name, NameData}; -use lean_ffi::nat::Nat; +use bignat::Nat; +use ix_common::env::{Name, NameData}; use rustc_hash::FxHashMap; /// Serialize a Name to bytes (full recursive serialization, for standalone use). @@ -1109,6 +1129,7 @@ impl Env { /// insertion orders), and DashMap iteration order is shard-dependent. /// Sorting the keys is the minimum work to guarantee that. pub fn put(&self, buf: &mut Vec) -> Result<(), String> { + #[cfg(not(target_arch = "riscv64"))] use rayon::slice::ParallelSliceMut; // Chatty per-section logging gated on IX_QUIET=1 (disables) so we can @@ -1131,7 +1152,10 @@ impl Env { // ───────────────────────────────────────────────────────────────────── let mut const_addrs: Vec
= self.consts.iter().map(|e| e.key().clone()).collect(); + #[cfg(not(target_arch = "riscv64"))] const_addrs.par_sort_unstable(); + #[cfg(target_arch = "riscv64")] + const_addrs.sort_unstable(); let root = merkle_root_canonical(&const_addrs).unwrap_or_else(zero_address); put_address(&root, buf); @@ -1144,7 +1168,10 @@ impl Env { } let mut blob_addrs: Vec
= self.blobs.iter().map(|e| e.key().clone()).collect(); + #[cfg(not(target_arch = "riscv64"))] blob_addrs.par_sort_unstable(); + #[cfg(target_arch = "riscv64")] + blob_addrs.sort_unstable(); put_u64(blob_addrs.len() as u64, buf); for addr in &blob_addrs { if let Some(entry) = self.blobs.get(addr) { @@ -1260,9 +1287,14 @@ impl Env { // Sort by the cached name hash bytes (same key used by every existing // Section 4 ordering guarantee). `par_sort_unstable_by` uses rayon to // parallelize the compare across all cores. + #[cfg(not(target_arch = "riscv64"))] named_keys.par_sort_unstable_by(|a, b| { a.get_hash().as_bytes().cmp(b.get_hash().as_bytes()) }); + #[cfg(target_arch = "riscv64")] + named_keys.sort_unstable_by(|a, b| { + a.get_hash().as_bytes().cmp(b.get_hash().as_bytes()) + }); if !quiet { eprintln!( "[Env::put] section 4/5 named: collected+sorted in {:.1}s, \ @@ -1297,7 +1329,10 @@ impl Env { } let mut comm_addrs: Vec
= self.comms.iter().map(|e| e.key().clone()).collect(); + #[cfg(not(target_arch = "riscv64"))] comm_addrs.par_sort_unstable(); + #[cfg(target_arch = "riscv64")] + comm_addrs.sort_unstable(); put_u64(comm_addrs.len() as u64, buf); for addr in &comm_addrs { if let Some(entry) = self.comms.get(addr) { @@ -1311,6 +1346,30 @@ impl Env { sec_start.elapsed().as_secs_f64(), buf.len(), ); + } + + // Optional trailing section: anon_hints (Address -> ReducibilityHints). + // `get_anon` normally HARVESTS hints from the Named section; a closure + // sub-env (built for shard injection) DROPS that section, which would + // force the kernel to `Regular(0)` and add a def-eq overhead vs whole-env + // proving. Carrying the hints here lets the guest reproduce vanilla kernel + // behavior exactly. Written only when populated, so compiler-produced + // `.ixe` (empty map) are byte-identical to before; loaders read it only if + // bytes remain after comms. Hints are performance-only (the `Regular(0)` + // fallback is always correct), so this section is intentionally NOT covered + // by the consts merkle root. + if !self.anon_hints.is_empty() { + let mut hint_addrs: Vec
= + self.anon_hints.keys().cloned().collect(); + hint_addrs.sort_unstable(); + put_u64(hint_addrs.len() as u64, buf); + for addr in &hint_addrs { + put_address(addr, buf); + self.anon_hints[addr].put_ser(buf); + } + } + + if !quiet { eprintln!( "[Env::put] ALL DONE: {} bytes in {:.1}s", buf.len(), @@ -1343,7 +1402,8 @@ impl Env { // recomputed value at the end of deserialization. let stored_root = get_address(buf)?; - let env = Env::new(); + #[cfg_attr(not(target_arch = "riscv64"), allow(unused_mut))] + let mut env = Env::new(); // Section 1: Blobs let num_blobs = get_u64(buf)?; @@ -1390,7 +1450,9 @@ impl Env { addr.hex() )); } - env.store_const_lazy(addr, bytes.into()); + env + .consts + .insert(addr, crate::lazy::LazyConstant::from_bytes(bytes.into())); } // Section 3: Names (build lookup table and reverse index for metadata) @@ -1431,6 +1493,9 @@ impl Env { env.comms.insert(addr, comm); } + // Optional trailing anon_hints section (see `Env::put`). + read_anon_hints_section(buf, &mut env)?; + // Verify the stored merkle root matches what we'd compute from // env.consts. Empty const set → expected = zero_address(). // Rejects any tampering with the header. @@ -1649,7 +1714,9 @@ impl Env { addr.hex() )); } - env.store_const_lazy(addr, bytes.into()); + env + .consts + .insert(addr, crate::lazy::LazyConstant::from_bytes(bytes.into())); } // Section 3: Names — parse and DISCARD. We still need a populated @@ -1694,6 +1761,11 @@ impl Env { let _comm = Comm::get(buf)?; } + // Optional trailing anon_hints section (see `Env::put`). For a closure + // sub-env this carries the hints the dropped Named section would have, so + // the kernel reproduces vanilla behavior with no def-eq overhead. + read_anon_hints_section(buf, &mut env)?; + drop(names_lookup); drop(name_reverse_index); @@ -1734,6 +1806,7 @@ impl Env { /// On Linux, the kernel's adaptive readahead handles the linear /// scan during section parsing efficiently; subsequent random /// access from worker kernel-check threads pages in as needed. + #[cfg(not(target_arch = "riscv64"))] pub fn get_anon_mmap(path: &std::path::Path) -> Result { let file = std::fs::File::open(path).map_err(|e| { format!("Env::get_anon_mmap: open {}: {e}", path.display()) @@ -1844,7 +1917,14 @@ impl Env { addr.hex() )); } - env.store_const_lazy_mmap(addr, Arc::clone(&mmap), offset, len); + env.consts.insert( + addr, + crate::lazy::LazyConstant::from_mmap_slice( + Arc::clone(&mmap), + offset, + len, + ), + ); buf = &buf[len..]; } @@ -1884,6 +1964,9 @@ impl Env { let _comm = Comm::get(&mut buf)?; } + // Optional trailing anon_hints section (see `Env::put`). + read_anon_hints_section(&mut buf, &mut env)?; + drop(names_lookup); drop(name_reverse_index); @@ -2003,8 +2086,9 @@ impl Env { /// Mathlib because 4.7M shard-lock acquisitions dominate vs the one-time /// ~150 MB tuple-clone allocation. fn topological_sort_names( - names: &dashmap::DashMap, + names: &crate::map::IxonMap, ) -> Vec<(Address, Name)> { + #[cfg(not(target_arch = "riscv64"))] use rayon::slice::ParallelSliceMut; use rustc_hash::FxHashSet; @@ -2043,7 +2127,10 @@ fn topological_sort_names( // during DFS). Parallel sort uses rayon over address bytes. let mut sorted_entries: Vec<(Address, Name)> = names.iter().map(|e| (e.key().clone(), e.value().clone())).collect(); + #[cfg(not(target_arch = "riscv64"))] sorted_entries.par_sort_unstable_by(|a, b| a.0.cmp(&b.0)); + #[cfg(target_arch = "riscv64")] + sorted_entries.sort_unstable_by(|a, b| a.0.cmp(&b.0)); for (_, name) in &sorted_entries { visit(name, &mut visited, &mut result); } @@ -2054,9 +2141,10 @@ fn topological_sort_names( #[cfg(test)] mod tests { use super::*; - use crate::ix::ixon::constant::tests::gen_constant; - use crate::ix::ixon::tests::gen_range; + use crate::constant::tests::gen_constant; + use crate::tests::gen_range; use quickcheck::{Arbitrary, Gen}; + use quickcheck_macros::quickcheck; #[quickcheck] fn prop_pack_bools_roundtrip(x: Vec) -> bool { @@ -2336,8 +2424,8 @@ mod tests { } fn defn_const_discriminator(refs: Vec
, lvls: u64) -> Constant { - use crate::ix::env::DefinitionSafety; - use crate::ix::ixon::constant::{DefKind, Definition}; + use crate::constant::{DefKind, Definition}; + use ix_common::env::DefinitionSafety; Constant::with_tables( ConstantInfo::Defn(Definition { kind: DefKind::Definition, @@ -2372,7 +2460,7 @@ mod tests { #[test] fn env_root_empty_env_is_zero_address() { - use crate::ix::ixon::merkle::zero_address; + use crate::merkle::zero_address; let env = Env::new(); let mut buf = Vec::new(); env.put(&mut buf).unwrap(); @@ -2386,7 +2474,7 @@ mod tests { #[test] fn env_root_present_when_consts_nonempty() { - use crate::ix::ixon::merkle::zero_address; + use crate::merkle::zero_address; let env = Env::new(); env.store_const(Address::hash(b"a"), defn_const(vec![])); let mut buf = Vec::new(); @@ -2524,7 +2612,7 @@ mod tests { #[test] fn get_anon_keeps_consts_drops_metadata() { - use crate::ix::ixon::env::Named; + use crate::env::Named; let env = Env::new(); // Round-trip tests must use canonical addresses (see store_canonical // helper); `Env::get`/`get_anon` reject entries whose bytes don't diff --git a/src/ix/ixon/sharing.rs b/crates/ixon/src/sharing.rs similarity index 99% rename from src/ix/ixon/sharing.rs rename to crates/ixon/src/sharing.rs index 610d07c2e..ccf87dd29 100644 --- a/src/ix/ixon/sharing.rs +++ b/crates/ixon/src/sharing.rs @@ -1060,7 +1060,7 @@ mod tests { #[test] fn test_roundtrip_with_sharing() { - use crate::ix::ixon::serialize::{get_expr, put_expr}; + use crate::serialize::{get_expr, put_expr}; // Create a simple expression with potential sharing let var0 = Expr::var(0); diff --git a/src/ix/ixon/tag.rs b/crates/ixon/src/tag.rs similarity index 99% rename from src/ix/ixon/tag.rs rename to crates/ixon/src/tag.rs index 240d96886..35465aaab 100644 --- a/src/ix/ixon/tag.rs +++ b/crates/ixon/src/tag.rs @@ -225,6 +225,7 @@ impl Tag0 { mod tests { use super::*; use quickcheck::{Arbitrary, Gen}; + use quickcheck_macros::quickcheck; // ============================================================================ // Arbitrary implementations diff --git a/src/ix/ixon/univ.rs b/crates/ixon/src/univ.rs similarity index 98% rename from src/ix/ixon/univ.rs rename to crates/ixon/src/univ.rs index ce3e9db8e..412823320 100644 --- a/src/ix/ixon/univ.rs +++ b/crates/ixon/src/univ.rs @@ -162,8 +162,9 @@ pub fn get_univ(buf: &mut &[u8]) -> Result, String> { #[cfg(test)] pub mod tests { use super::*; - use crate::ix::ixon::tests::{gen_range, next_case}; + use crate::tests::{gen_range, next_case}; use quickcheck::{Arbitrary, Gen}; + use quickcheck_macros::quickcheck; use std::ptr; #[derive(Clone, Copy)] diff --git a/crates/kernel/Cargo.toml b/crates/kernel/Cargo.toml new file mode 100644 index 000000000..edaef73dd --- /dev/null +++ b/crates/kernel/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "ix-kernel" +version.workspace = true +edition.workspace = true +license.workspace = true + +[dependencies] +bignat = { workspace = true } +blake3 = { workspace = true } +dashmap = { workspace = true } +indexmap = { workspace = true } +ix-common = { workspace = true } +ixon = { workspace = true } +itertools = { workspace = true } +log = { workspace = true } +num-bigint = { workspace = true } +rustc-hash = { workspace = true } + +[target.'cfg(not(target_arch = "riscv64"))'.dependencies] +dashmap = { workspace = true, features = ["rayon"] } +rayon = { workspace = true } + +[dev-dependencies] +quickcheck = { workspace = true } +quickcheck_macros = { workspace = true } + +[lints] +workspace = true diff --git a/src/ix/kernel/anon_env.rs b/crates/kernel/src/anon_env.rs similarity index 95% rename from src/ix/kernel/anon_env.rs rename to crates/kernel/src/anon_env.rs index cfc18168d..fe8375f78 100644 --- a/src/ix/kernel/anon_env.rs +++ b/crates/kernel/src/anon_env.rs @@ -24,9 +24,9 @@ use rustc_hash::FxHashSet; use std::collections::VecDeque; use std::sync::Arc; -use crate::ix::address::Address; -use crate::ix::ixon::constant::Constant; -use crate::ix::ixon::env::Env as IxonEnv; +use ix_common::address::Address; +use ixon::constant::Constant; +use ixon::env::Env as IxonEnv; /// Anonymous-only view over an `IxonEnv`. #[derive(Clone, Copy, Debug)] @@ -103,10 +103,10 @@ impl<'a> AnonEnv<'a> { #[cfg(test)] mod tests { use super::*; - use crate::ix::env::Name; - use crate::ix::ixon::constant::{Axiom, ConstantInfo}; - use crate::ix::ixon::env::Named; - use crate::ix::ixon::expr::Expr; + use ix_common::env::Name; + use ixon::constant::{Axiom, ConstantInfo}; + use ixon::env::Named; + use ixon::expr::Expr; fn axiom_const_with_refs(refs: Vec
) -> Constant { Constant::with_tables( diff --git a/crates/kernel/src/anon_work.rs b/crates/kernel/src/anon_work.rs new file mode 100644 index 000000000..be0aae24a --- /dev/null +++ b/crates/kernel/src/anon_work.rs @@ -0,0 +1,326 @@ +//! Anon-mode work enumeration over `IxonEnv::consts`. +//! +//! Identifies the set of kernel-checkable target addresses without +//! consulting any metadata sections (`named`/`names`/`comms`), so it +//! works against an env loaded via [`ixon::env::Env::get_anon`]. +//! +//! The set produced here is the canonical work set for anon-mode +//! typechecking: it matches `rs_kernel_check_anon` in +//! `crates/ffi/src/kernel.rs` and is what an Aiur-style verifier +//! commits to. Callers iterate the returned `Vec` and +//! invoke `TypeChecker::check_const` on each item's `primary` address; +//! the kernel's internal block coordination handles checking every +//! member + ctor of `Block` items. +//! +//! ## Enumeration rules +//! +//! For each entry in `env.consts`: +//! - Projection variants (IPrj/CPrj/RPrj/DPrj) are skipped — they're +//! covered by checking their parent Muts block. +//! - Standalone variants (Defn/Recr/Axio/Quot) emit one `Standalone` +//! work item with the constant's own address. +//! - Muts blocks materialize to enumerate members, then emit one +//! `Block` item whose `primary` is the first member's projection +//! address (and `targets` includes that primary plus every other +//! member's projection plus one CPrj per constructor of each +//! inductive member). Checking the primary triggers the kernel's +//! block-coordination logic which covers every target. +//! +//! Dispatch on the outer `Tag4` byte via +//! [`ixon::lazy::LazyConstant::peek_variant`] avoids body parse + +//! `Arc` allocation for the ~95% of constants that are +//! standalones or projections. + +use ix_common::address::Address; +use ixon::env::Env as IxonEnv; +use ixon::lazy::ConstVariantTag; + +use crate::ingress::{ + anon_ctor_proj_addr, anon_defn_proj_addr, anon_indc_proj_addr, + anon_recr_proj_addr, +}; + +/// A single anon-mode work item — one `tc.check_const(primary)` per +/// item suffices to typecheck every address in `targets`. +#[derive(Clone, Debug)] +pub enum AnonWorkItem { + /// A standalone (Defn/Recr/Axio/Quot) constant. `addr` is both the + /// kernel-checked target and the only address this item covers. + Standalone { addr: Address }, + /// A Muts block. Checking `primary` (the first member's projection + /// address) drives the kernel's block coordination, which in turn + /// typechecks every entry in `targets`. `targets[0] == primary`. + /// `block_addr` is the block's own `env.consts` key (the `Tag::Muts` + /// entry) — distinct from the projection `targets`, and the address + /// other constants' `refs` use to reference the block. Carried so the + /// set of `consts` keys a checked item certifies ([`proven_targets`]) + /// is in the same address space as `Constant.refs`. + /// + /// [`proven_targets`]: AnonWorkItem::proven_targets + Block { block_addr: Address, primary: Address, targets: Vec
}, +} + +impl AnonWorkItem { + /// The address to pass to `tc.check_const`. + pub fn primary(&self) -> &Address { + match self { + AnonWorkItem::Standalone { addr } => addr, + AnonWorkItem::Block { primary, .. } => primary, + } + } + + /// Every kernel-checked target this item covers (one for + /// `Standalone`, `targets.len()` for `Block`). + pub fn targets(&self) -> &[Address] { + match self { + AnonWorkItem::Standalone { addr } => core::slice::from_ref(addr), + AnonWorkItem::Block { targets, .. } => targets, + } + } + + /// Every `env.consts` key this item certifies (proves) well-typed when + /// its `primary` is checked — the *proven targets*, in the same address + /// space as `Constant.refs`, so a dependency ref can be matched against + /// the union of `proven_targets()` over all checked items: refs in that + /// union are already certified, the rest are the claim's open assumptions + /// (resolved later against other proofs). Unlike + /// [`targets`](Self::targets) (the addresses the kernel actually walks), + /// a `Block`'s proven set also includes the block's own `consts` key: + /// for a `Standalone` it's just its address; for a `Block` it's + /// `block_addr` plus every projection target (members + ctors). The + /// union of `proven_targets()` over a full `build_anon_work` is exactly + /// `env.consts.keys()`. + pub fn proven_targets(&self) -> Vec
{ + match self { + AnonWorkItem::Standalone { addr } => vec![addr.clone()], + AnonWorkItem::Block { block_addr, targets, .. } => { + let mut v = Vec::with_capacity(1 + targets.len()); + v.push(block_addr.clone()); + v.extend(targets.iter().cloned()); + v + }, + } + } +} + +/// Enumerate the anon-mode kernel work set from `env.consts`. +/// +/// Returns one `AnonWorkItem` per kernel-checkable group of +/// constants. The total number of checked target addresses is +/// `work.iter().map(|w| w.targets().len()).sum()`. +/// +/// Errors only on a corrupted env (missing const at an enumerated +/// address, or a Tag4 head byte that doesn't correspond to a known +/// `ConstantInfo` variant). +pub fn build_anon_work(env: &IxonEnv) -> Result, String> { + use ConstVariantTag as Tag; + use ixon::constant::ConstantInfo as CI; + use ixon::constant::MutConst as MC; + + let mut work: Vec = Vec::new(); + + // Sort keys for deterministic ordering across runs. + let mut keys: Vec
= + env.consts.iter().map(|e| e.key().clone()).collect(); + keys.sort_unstable(); + + for addr in keys { + let lc = env.consts.get(&addr).ok_or_else(|| { + format!("build_anon_work: missing const at {}", addr.hex()) + })?; + let tag = lc.value().peek_variant().map_err(|e| { + format!("build_anon_work: peek_variant {}: {e}", addr.hex()) + })?; + match tag { + Tag::IPrj | Tag::CPrj | Tag::RPrj | Tag::DPrj => { + // Skip — covered by parent block. + }, + Tag::Defn | Tag::Recr | Tag::Axio | Tag::Quot => { + work.push(AnonWorkItem::Standalone { addr: addr.clone() }); + }, + Tag::Muts => { + // Materialize once to enumerate members; the `Arc` + // drops at the end of this arm — no cache retention. + let constant = lc.value().get().map_err(|e| { + format!("build_anon_work: materialize Muts {}: {e}", addr.hex()) + })?; + let CI::Muts(members) = &constant.info else { + return Err(format!( + "build_anon_work: Tag::Muts but ConstantInfo is {:?} at {}", + constant.info.variant(), + addr.hex() + )); + }; + let mut targets: Vec
= Vec::new(); + for (i, member) in members.iter().enumerate() { + let i = i as u64; + let member_addr = match member { + MC::Defn(_) => anon_defn_proj_addr(&addr, i), + MC::Indc(_) => anon_indc_proj_addr(&addr, i), + MC::Recr(_) => anon_recr_proj_addr(&addr, i), + }; + targets.push(member_addr); + if let MC::Indc(ind) = member { + for cidx in 0..ind.ctors.len() as u64 { + targets.push(anon_ctor_proj_addr(&addr, i, cidx)); + } + } + } + if targets.is_empty() { + continue; + } + let primary = targets[0].clone(); + work.push(AnonWorkItem::Block { + block_addr: addr.clone(), + primary, + targets, + }); + }, + } + } + + Ok(work) +} + +/// The full dependency-closure address set of `roots`: their transitive Expr +/// `refs` PLUS each projection's structural `block: Address`. A refs-only walk +/// misses the block — projections (IPrj/CPrj/RPrj/DPrj) point at their Muts +/// block via a struct field, not the Expr ref table — so the kernel couldn't +/// resolve the projection and would spuriously fail. The returned set includes +/// constant AND referenced-blob addresses (external refs absent from `source` +/// are included as addresses but have no bytes to copy). +pub fn closure_addrs( + source: &IxonEnv, + roots: &[Address], +) -> std::collections::HashSet
{ + use std::collections::{HashSet, VecDeque}; + + use ixon::constant::{ConstantInfo as CI, MutConst as MC}; + + let proj_block = |info: &CI| -> Option
{ + match info { + CI::IPrj(p) => Some(p.block.clone()), + CI::CPrj(p) => Some(p.block.clone()), + CI::RPrj(p) => Some(p.block.clone()), + CI::DPrj(p) => Some(p.block.clone()), + _ => None, + } + }; + + let mut closure: HashSet
= HashSet::default(); + let mut queue: VecDeque
= VecDeque::new(); + let push = + |closure: &mut HashSet
, q: &mut VecDeque
, a: Address| { + if closure.insert(a.clone()) { + q.push_back(a); + } + }; + for r in roots { + push(&mut closure, &mut queue, r.clone()); + } + while let Some(addr) = queue.pop_front() { + if let Some(c) = source.get_const(&addr) { + // 1. Expr-level refs. + for r in &c.refs { + push(&mut closure, &mut queue, r.clone()); + } + // 2. A projection → its Muts block (structural, not in `refs`). + if let Some(b) = proj_block(&c.info) { + push(&mut closure, &mut queue, b); + } + // 3. A Muts block → ALL its member + constructor projection entries. + // Ingressing a block (`ingress_anon_block`) computes these projection + // addresses and requires them present, even when nothing references them + // directly via `refs`. Mirror `build_anon_work`'s enumeration so the + // sub-env carries them (else the guest fails with "computed CPrj address + // … not present in env"). + if let CI::Muts(members) = &c.info { + for (i, m) in members.iter().enumerate() { + let i = i as u64; + let member_addr = match m { + MC::Defn(_) => anon_defn_proj_addr(&addr, i), + MC::Indc(_) => anon_indc_proj_addr(&addr, i), + MC::Recr(_) => anon_recr_proj_addr(&addr, i), + }; + push(&mut closure, &mut queue, member_addr); + if let MC::Indc(ind) = m { + for cidx in 0..ind.ctors.len() as u64 { + push( + &mut closure, + &mut queue, + anon_ctor_proj_addr(&addr, i, cidx), + ); + } + } + } + } + } + } + closure +} + +/// Build a closure sub-env: serialize only the BFS dependency closure of +/// `roots` (their transitive `Constant.refs` plus referenced literal blobs), +/// copying each constant's GENUINE bytes via `store_const_lazy` so the guest's +/// per-const integrity check (`hash(bytes) == addr`) and the env merkle root +/// still hold. The guest decodes this instead of the whole env, so it pays only +/// its closure's decode — essential for envs that don't fit the guest whole +/// (Init, 184 MB doesn't fit the 512 MB Zisk guest). Empty `anon_hints` (no +/// metadata section) is performance-only: ingress falls back to `Regular(0)`, +/// so the typecheck result — and thus the committed claim — is unchanged. +/// External refs (not in `source`) are omitted and remain open assumptions, +/// exactly as in whole-env. +/// +/// Host-only: builds an `Env` via the `store_*` helpers, which are gated to +/// non-riscv64 (see `Env::store_blob`). The guest receives an already-built +/// sub-env from the host and only enumerates it via `build_anon_work`. +#[cfg(not(target_arch = "riscv64"))] +pub fn build_sub_env( + source: &IxonEnv, + roots: &[Address], +) -> Result, String> { + let closure = closure_addrs(source, roots); + let mut sub = IxonEnv::new(); + for addr in &closure { + if let Some(bytes) = source.get_const_bytes(addr) { + sub.store_const_lazy(addr.clone(), bytes); + } else if let Some(blob) = source.get_blob(addr) { + sub.store_blob(blob); + } + // else: external ref absent from `source` — omit; stays an open assumption. + // Carry the constant's reducibility hint so the guest reproduces vanilla + // kernel behavior. `get_anon` normally harvests hints from the Named + // section, which this sub-env drops; without them the kernel forces + // `Regular(0)` and does extra def-eq work (the ~30% check overhead). + if let Some(h) = source.anon_hints.get(addr) { + sub.anon_hints.insert(addr.clone(), *h); + } + } + let mut buf = Vec::new(); + sub.put(&mut buf).map_err(|e| format!("sub-env serialize: {e}"))?; + Ok(buf) +} + +/// The ingress-block address that owns `addr`: a projection (IPrj/CPrj/RPrj/ +/// DPrj) maps to its Muts `block`; anything else is its own block. Used to map +/// a resolved constant address to the `build_anon_work` item that covers it +/// (standalone → itself; a mutual-block member → the whole block). +pub fn block_of_addr(env: &IxonEnv, addr: &Address) -> Address { + use ixon::constant::ConstantInfo as CI; + match env.get_const(addr) { + Some(c) => match &c.info { + CI::IPrj(p) => p.block.clone(), + CI::CPrj(p) => p.block.clone(), + CI::RPrj(p) => p.block.clone(), + CI::DPrj(p) => p.block.clone(), + _ => addr.clone(), + }, + None => addr.clone(), + } +} + +/// The ingress-block address of a work item's `primary` — the key for matching +/// a resolved target constant to the work item that covers it. +pub fn work_block_addr(env: &IxonEnv, item: &AnonWorkItem) -> Address { + block_of_addr(env, item.primary()) +} diff --git a/src/ix/kernel/canonical_check.rs b/crates/kernel/src/canonical_check.rs similarity index 99% rename from src/ix/kernel/canonical_check.rs rename to crates/kernel/src/canonical_check.rs index 68429f993..9c028fecd 100644 --- a/src/ix/kernel/canonical_check.rs +++ b/crates/kernel/src/canonical_check.rs @@ -38,7 +38,7 @@ use std::cmp::Ordering; use rustc_hash::FxHashMap; -use crate::ix::address::Address; +use ix_common::address::Address; use super::constant::{KConst, RecRule}; use super::error::TcError; @@ -47,7 +47,7 @@ use super::id::KId; use super::level::{KUniv, UnivData}; use super::mode::KernelMode; -pub use crate::ix::strong_ordering::SOrd; +pub use ix_common::strong_ordering::SOrd; // =========================================================================== // KMutCtx — block-local address → class-index map @@ -415,11 +415,11 @@ fn compare_krecr( /// alpha-collapse on the canonical IXON form, which doesn't include /// hints (and treats safety as a separate sidecar in practice). fn compare_kdefn( - x_kind: crate::ix::ixon::constant::DefKind, + x_kind: ixon::constant::DefKind, x_lvls: u64, x_ty: &KExpr, x_val: &KExpr, - y_kind: crate::ix::ixon::constant::DefKind, + y_kind: ixon::constant::DefKind, y_lvls: u64, y_ty: &KExpr, y_val: &KExpr, @@ -826,10 +826,10 @@ pub fn validate_canonical_block_single_pass( #[cfg(test)] mod tests { use super::*; - use crate::ix::address::Address; - use crate::ix::env::{BinderInfo, Name}; - use crate::ix::env::{DefinitionSafety, ReducibilityHints}; - use crate::ix::ixon::constant::DefKind; + use ix_common::address::Address; + use ix_common::env::{BinderInfo, Name}; + use ix_common::env::{DefinitionSafety, ReducibilityHints}; + use ixon::constant::DefKind; use super::super::expr::KExpr; use super::super::level::KUniv; diff --git a/src/ix/kernel/check.rs b/crates/kernel/src/check.rs similarity index 96% rename from src/ix/kernel/check.rs rename to crates/kernel/src/check.rs index 0b0f97645..094ecf5e0 100644 --- a/src/ix/kernel/check.rs +++ b/crates/kernel/src/check.rs @@ -1,13 +1,12 @@ //! Constant checking dispatch. -use std::sync::LazyLock; use std::time::{Duration, Instant}; use rustc_hash::FxHashSet; -use crate::ix::address::Address; -use crate::ix::env::{DefinitionSafety, QuotKind}; -use crate::ix::ixon::constant::DefKind; +use ix_common::address::Address; +use ix_common::env::{DefinitionSafety, QuotKind}; +use ixon::constant::DefKind; use super::constant::KConst; use super::env::Addr; @@ -26,14 +25,20 @@ use super::tc::TypeChecker; /// `IX_DECL_DIFF=1` we dump `val_ty` / `ty` and their whnf forms to /// pinpoint which sub-expression is stuck \u2014 sister tool to /// `IX_APP_DIFF` in `infer.rs`. -static IX_DECL_DIFF: LazyLock = - LazyLock::new(|| std::env::var("IX_DECL_DIFF").is_ok()); +#[cfg(not(target_arch = "riscv64"))] +static IX_DECL_DIFF: crate::EnvFlag = + crate::EnvFlag::new(|| crate::env_var("IX_DECL_DIFF").is_ok()); +#[cfg(target_arch = "riscv64")] +static IX_DECL_DIFF: crate::EnvFlag = crate::EnvFlag::new(|| false); /// Per-phase timing for `Defn` checks. Set `IX_PHASE_TIMING=1` to see where a /// slow constant spends its time. Noisy — gate on a single constant via focus /// mode so only one line is printed. -static IX_PHASE_TIMING: LazyLock = - LazyLock::new(|| std::env::var("IX_PHASE_TIMING").is_ok()); +#[cfg(not(target_arch = "riscv64"))] +static IX_PHASE_TIMING: crate::EnvFlag = + crate::EnvFlag::new(|| crate::env_var("IX_PHASE_TIMING").is_ok()); +#[cfg(target_arch = "riscv64")] +static IX_PHASE_TIMING: crate::EnvFlag = crate::EnvFlag::new(|| false); #[derive(Clone, Copy, Debug, Default)] struct ValidationTiming { @@ -67,7 +72,7 @@ impl TypeChecker<'_, M> { /// Type-check a single constant. Clears per-constant caches first. pub fn check_const(&mut self, id: &KId) -> Result<(), TcError> where - M::MField>: CheckDupLevelParams, + M::MField>: CheckDupLevelParams, { let c = self.get_const(id)?; if let Some(block) = self.coordinated_block_for(&c)? { @@ -84,7 +89,7 @@ impl TypeChecker<'_, M> { fn check_const_member_fresh(&mut self, id: &KId) -> Result<(), TcError> where - M::MField>: CheckDupLevelParams, + M::MField>: CheckDupLevelParams, { self.reset(); self.begin_const(id); @@ -99,7 +104,7 @@ impl TypeChecker<'_, M> { c: &KConst, ) -> Result<(), TcError> where - M::MField>: CheckDupLevelParams, + M::MField>: CheckDupLevelParams, { let phase_timing = *IX_PHASE_TIMING; let overall = if phase_timing { Some(Instant::now()) } else { None }; @@ -154,16 +159,16 @@ impl TypeChecker<'_, M> { // (delta, iota, native, ...) is missing for convergence. let val_ty_whnf = self.whnf(&val_ty); let ty_whnf = self.whnf(ty); - eprintln!("[decl diff] DeclTypeMismatch"); - eprintln!(" val_ty: {val_ty}"); - eprintln!(" ty: {ty}"); + log::info!("[decl diff] DeclTypeMismatch"); + log::info!(" val_ty: {val_ty}"); + log::info!(" ty: {ty}"); match &val_ty_whnf { - Ok(w) => eprintln!(" val_ty whnf: {w}"), - Err(e) => eprintln!(" val_ty whnf: ERR {e}"), + Ok(w) => log::info!(" val_ty whnf: {w}"), + Err(e) => log::info!(" val_ty whnf: ERR {e}"), } match &ty_whnf { - Ok(w) => eprintln!(" ty whnf: {w}"), - Err(e) => eprintln!(" ty whnf: ERR {e}"), + Ok(w) => log::info!(" ty whnf: {w}"), + Err(e) => log::info!(" ty whnf: ERR {e}"), } } return Err(TcError::DeclTypeMismatch); @@ -187,7 +192,7 @@ impl TypeChecker<'_, M> { if let Some(t0) = overall && self.phase_timing_label_matches(id) { - eprintln!( + log::info!( "[phase] {} total={:>8.1?} dup_lvls={:>8.1?} validate={:>8.1?} validate_ty={:>8.1?} validate_val={:>8.1?} validate_rules={:>8.1?} validate_univ={:>8.1?} infer_ty={:>8.1?} infer_val={:>8.1?} def_eq={:>8.1?} safety={:>8.1?} safety_ty={:>8.1?} safety_val={:>8.1?}", id, t0.elapsed(), @@ -335,7 +340,7 @@ impl TypeChecker<'_, M> { requested: &KId, ) -> Result<(), TcError> where - M::MField>: CheckDupLevelParams, + M::MField>: CheckDupLevelParams, { let phase_timing = *IX_PHASE_TIMING; let overall = if phase_timing { Some(Instant::now()) } else { None }; @@ -390,7 +395,7 @@ impl TypeChecker<'_, M> { if let Some(t0) = overall && self.phase_timing_label_matches(block) { - eprintln!( + log::info!( "[phase-block] {} kind={:?} members={} total={:>8.1?} get_members={:>8.1?} prevalidate={:>8.1?} validate_ty={:>8.1?} validate_val={:>8.1?} validate_rules={:>8.1?} validate_univ={:>8.1?} classify={:>8.1?} body={:>8.1?}", block, kind, @@ -479,7 +484,7 @@ impl TypeChecker<'_, M> { } fn phase_timing_label_matches(&self, id: &KId) -> bool { - match std::env::var("IX_KERNEL_DEBUG_CONST") { + match crate::env_var("IX_KERNEL_DEBUG_CONST") { Ok(filter) if filter.is_empty() => true, Ok(filter) => { id.to_string().contains(&filter) @@ -867,15 +872,15 @@ mod tests { use super::super::level::KUniv; use super::super::mode::Anon; use super::super::tc::TypeChecker; - use crate::ix::address::Address; - use crate::ix::env::{DefinitionSafety, ReducibilityHints}; - use crate::ix::ixon::constant::DefKind; + use ix_common::address::Address; + use ix_common::env::{DefinitionSafety, ReducibilityHints}; + use ixon::constant::DefKind; #[test] fn profile_sink_records_delta_edge_and_fuel() { - use crate::ix::kernel::mode::Meta; - use crate::ix::kernel::testing as t; - use crate::ix::profile::ProfileSink; + use crate::mode::Meta; + use crate::profile::ProfileSink; + use crate::testing as t; // g : Sort 2 := Sort 1 — a Definition, delta-reducible to Sort 1. let (g_id, g) = t::mk_defn( @@ -1139,13 +1144,13 @@ mod tests { #[test] fn check_duplicate_level_params_rejected() { - use crate::ix::kernel::mode::Meta; + use crate::mode::Meta; type ME = KExpr; type MU = KUniv; let mut env = KEnv::::new(); let dup_name = - crate::ix::env::Name::str(crate::ix::env::Name::anon(), "u".into()); + ix_common::env::Name::str(ix_common::env::Name::anon(), "u".into()); let id = KId::new(mk_addr("T"), dup_name.clone()); env.insert( id.clone(), diff --git a/src/ix/kernel/claim.rs b/crates/kernel/src/claim.rs similarity index 95% rename from src/ix/kernel/claim.rs rename to crates/kernel/src/claim.rs index 301300c70..8d6dc65b6 100644 --- a/src/ix/kernel/claim.rs +++ b/crates/kernel/src/claim.rs @@ -12,11 +12,11 @@ use rustc_hash::FxHashSet; -use crate::ix::address::Address; -use crate::ix::ixon::constant::ConstantInfo; -use crate::ix::ixon::env::Env; -use crate::ix::ixon::merkle::merkle_root_canonical; -use crate::ix::ixon::proof::Claim; +use ix_common::address::Address; +use ixon::constant::ConstantInfo; +use ixon::env::Env; +use ixon::merkle::merkle_root_canonical; +use ixon::proof::Claim; /// Canonical merkle root over the env's `consts.keys()`. Also called /// from the env serializer. Returns `None` for an empty const set. @@ -91,12 +91,10 @@ pub fn build_check_env_claim(env: &Env) -> Option { #[cfg(test)] mod tests { use super::*; - use crate::ix::env::DefinitionSafety; - use crate::ix::ixon::constant::{ - Axiom, Constant, ConstantInfo, DefKind, Definition, - }; - use crate::ix::ixon::expr::Expr; - use crate::ix::ixon::merkle::leaf_hash; + use ix_common::env::DefinitionSafety; + use ixon::constant::{Axiom, Constant, ConstantInfo, DefKind, Definition}; + use ixon::expr::Expr; + use ixon::merkle::leaf_hash; use std::sync::Arc; fn axiom_const(refs: Vec
) -> Constant { diff --git a/src/ix/kernel/congruence.rs b/crates/kernel/src/congruence.rs similarity index 97% rename from src/ix/kernel/congruence.rs rename to crates/kernel/src/congruence.rs index 2ba3ff44f..906ae1b29 100644 --- a/src/ix/kernel/congruence.rs +++ b/crates/kernel/src/congruence.rs @@ -3,8 +3,8 @@ //! Validates that Ixon ingress in Anon mode produces structurally correct //! constants by comparing the Lean `ConstantInfo` against the loaded `KConst`. -use crate::ix::address::Address; -use crate::ix::env::{self as lean, ConstantInfo as LeanCI, Literal, Name}; +use ix_common::address::Address; +use ix_common::env::{self as lean, ConstantInfo as LeanCI, Literal, Name}; use super::constant::KConst; use super::expr::{ExprData, KExpr}; @@ -17,7 +17,7 @@ pub struct NameResolver { } impl NameResolver { - pub fn from_ixon_env(ixon_env: &crate::ix::ixon::env::Env) -> Self { + pub fn from_ixon_env(ixon_env: &ixon::env::Env) -> Self { let mut map = rustc_hash::FxHashMap::default(); for entry in ixon_env.named.iter() { map.insert(entry.key().clone(), entry.value().addr.clone()); @@ -364,22 +364,22 @@ fn zero_const_tag(c: &KConst) -> &'static str { #[cfg(test)] mod tests { use super::*; - use crate::ix::address::Address; - use crate::ix::env::{ + use crate::constant::KConst; + use crate::id::KId; + use crate::mode::Anon; + use ix_common::address::Address; + use ix_common::env::{ self, AxiomVal, BinderInfo, ConstantVal, ConstructorVal, DefinitionSafety, DefinitionVal, InductiveVal, Level as LL, Name, OpaqueVal, QuotKind, QuotVal, RecursorRule as LeanRule, RecursorVal, ReducibilityHints, TheoremVal, }; - use crate::ix::ixon::env::{Env as IxonEnv, Named}; - use crate::ix::kernel::constant::KConst; - use crate::ix::kernel::id::KId; - use crate::ix::kernel::mode::Anon; + use ixon::env::{Env as IxonEnv, Named}; /// `Nat` from a u64 via the public `From` impl. /// (The `Nat` type itself is a private re-export in `env.rs`.) - fn n(x: u64) -> lean_ffi::nat::Nat { - lean_ffi::nat::Nat::from(x) + fn n(x: u64) -> bignat::Nat { + bignat::Nat::from(x) } // ---- test helpers ---- @@ -725,7 +725,7 @@ mod tests { let kc = KConst::::Defn { name: (), level_params: (), - kind: crate::ix::ixon::constant::DefKind::Definition, + kind: ixon::constant::DefKind::Definition, safety: DefinitionSafety::Safe, hints: ReducibilityHints::Opaque, lvls: 0, @@ -768,7 +768,7 @@ mod tests { let kc = KConst::::Defn { name: (), level_params: (), - kind: crate::ix::ixon::constant::DefKind::Definition, + kind: ixon::constant::DefKind::Definition, safety: DefinitionSafety::Safe, hints: ReducibilityHints::Opaque, lvls: 0, @@ -969,7 +969,7 @@ mod tests { let k = KConst::::Defn { name: (), level_params: (), - kind: crate::ix::ixon::constant::DefKind::Theorem, + kind: ixon::constant::DefKind::Theorem, safety: DefinitionSafety::Safe, hints: ReducibilityHints::Opaque, lvls: 0, diff --git a/src/ix/kernel/constant.rs b/crates/kernel/src/constant.rs similarity index 95% rename from src/ix/kernel/constant.rs rename to crates/kernel/src/constant.rs index 09bde5f0e..16a3592b1 100644 --- a/src/ix/kernel/constant.rs +++ b/crates/kernel/src/constant.rs @@ -3,8 +3,8 @@ //! Each variant carries structural fields plus metadata fields //! (`name`, `level_params`, `lean_all`) for roundtrip fidelity in Meta mode. -use crate::ix::env::{DefinitionSafety, Name, QuotKind, ReducibilityHints}; -use crate::ix::ixon::constant::DefKind; +use ix_common::env::{DefinitionSafety, Name, QuotKind, ReducibilityHints}; +use ixon::constant::DefKind; use super::expr::KExpr; use super::id::KId; @@ -153,9 +153,9 @@ mod tests { use super::super::level::KUniv; use super::super::mode::Anon; use super::*; - use crate::ix::address::Address; - use crate::ix::env::{DefinitionSafety, QuotKind, ReducibilityHints}; - use crate::ix::ixon::constant::DefKind; + use ix_common::address::Address; + use ix_common::env::{DefinitionSafety, QuotKind, ReducibilityHints}; + use ixon::constant::DefKind; fn sort0() -> KExpr { KExpr::sort(KUniv::zero()) diff --git a/src/ix/kernel/def_eq.rs b/crates/kernel/src/def_eq.rs similarity index 96% rename from src/ix/kernel/def_eq.rs rename to crates/kernel/src/def_eq.rs index a93233394..e546a3648 100644 --- a/src/ix/kernel/def_eq.rs +++ b/crates/kernel/src/def_eq.rs @@ -7,9 +7,7 @@ //! 4. Iterative lazy delta with same-head-spine optimization //! 5. Full WHNF, structural comparison, eta, struct eta -use std::sync::LazyLock; - -use crate::ix::ixon::constant::DefKind; +use ixon::constant::DefKind; use super::constant::KConst; use super::env::Addr; @@ -29,28 +27,28 @@ use super::tc::{ /// to watch all `Int.bmod`-involving comparisons). Prints `[deq] a b` /// before entering `is_def_eq_inner`, then the boolean outcome. Useful for /// pinning down which sub-expression of an App-spine is stuck. -static IX_DEF_EQ_TRACE: LazyLock> = - LazyLock::new(|| std::env::var("IX_DEF_EQ_TRACE").ok()); +static IX_DEF_EQ_TRACE: crate::EnvString = + crate::EnvString::new(|| crate::env_var("IX_DEF_EQ_TRACE").ok()); /// Global perf counter: total `is_def_eq` entries across all checks. /// When `IX_DEF_EQ_COUNT_LOG=1`, logs every 1M calls. Useful for /// detecting checks that explode into millions of recursive /// comparisons \u2014 a signal that some caching optimization is /// mis-firing or some reduction is looping. -static IX_DEF_EQ_COUNT_LOG: LazyLock = - LazyLock::new(|| std::env::var("IX_DEF_EQ_COUNT_LOG").is_ok()); +static IX_DEF_EQ_COUNT_LOG: crate::EnvFlag = + crate::EnvFlag::new(|| crate::env_var("IX_DEF_EQ_COUNT_LOG").is_ok()); /// Dump the expression pair when `is_def_eq` hits its recursion/fuel guard. /// The optional env var value is used as a substring filter over the two head /// constants; an empty value dumps every guard hit. -static IX_DEF_EQ_MAX_DUMP: LazyLock> = - LazyLock::new(|| std::env::var("IX_DEF_EQ_MAX_DUMP").ok()); +static IX_DEF_EQ_MAX_DUMP: crate::EnvString = + crate::EnvString::new(|| crate::env_var("IX_DEF_EQ_MAX_DUMP").ok()); -static IX_ETA_TRACE: LazyLock> = - LazyLock::new(|| std::env::var("IX_ETA_TRACE").ok()); +static IX_ETA_TRACE: crate::EnvString = + crate::EnvString::new(|| crate::env_var("IX_ETA_TRACE").ok()); -static IX_PROJ_DELTA_TRACE: LazyLock> = - LazyLock::new(|| std::env::var("IX_PROJ_DELTA_TRACE").ok()); +static IX_PROJ_DELTA_TRACE: crate::EnvString = + crate::EnvString::new(|| crate::env_var("IX_PROJ_DELTA_TRACE").ok()); static DEF_EQ_COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); @@ -65,9 +63,10 @@ impl TypeChecker<'_, M> { if *IX_DEF_EQ_COUNT_LOG { let n = DEF_EQ_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed); if n.is_multiple_of(100_000) && n > 0 { - eprintln!("[is_def_eq] count={n}"); + log::info!("[is_def_eq] count={n}"); } } + crate::profile::bump_def_eq(); if a.ptr_eq(b) { return Ok(true); } @@ -87,12 +86,12 @@ impl TypeChecker<'_, M> { let a_hit = head_const_name(a).is_some_and(|n| n.contains(prefix)); let b_hit = head_const_name(b).is_some_and(|n| n.contains(prefix)); if a_hit || b_hit { - eprintln!( + log::info!( "[deq] depth={} a={}", self.def_eq_depth, compact_def_eq_expr(a) ); - eprintln!( + log::info!( "[deq] depth={} b={}", self.def_eq_depth, compact_def_eq_expr(b) @@ -120,8 +119,8 @@ impl TypeChecker<'_, M> { // per method call. Any true result moves the originals into `add_equiv` // before returning. let eq_ctx = self.def_eq_ctx_key(a, b); - let a_key: crate::ix::kernel::equiv::EqKey = (a.hash_key(), eq_ctx); - let b_key: crate::ix::kernel::equiv::EqKey = (b.hash_key(), eq_ctx); + let a_key: crate::equiv::EqKey = (a.hash_key(), eq_ctx); + let b_key: crate::equiv::EqKey = (b.hash_key(), eq_ctx); if self.equiv_manager.is_equiv(&a_key, &b_key) { return Ok(true); @@ -211,7 +210,7 @@ impl TypeChecker<'_, M> { let ok = result?; if trace_active { - eprintln!( + log::info!( "[deq] depth={} -> {} ({})", self.def_eq_depth, ok, @@ -220,8 +219,8 @@ impl TypeChecker<'_, M> { // On FAIL, also dump the full a/b that failed (post-Tier-1 quick). // Lets us see what the def-eq engine actually compared. if !ok { - eprintln!("[deq fail] depth={} a-full: {a}", self.def_eq_depth); - eprintln!("[deq fail] depth={} b-full: {b}", self.def_eq_depth); + log::info!("[deq fail] depth={} a-full: {a}", self.def_eq_depth); + log::info!("[deq fail] depth={} b-full: {b}", self.def_eq_depth); } self.def_eq_trace_depth = self.def_eq_trace_depth.saturating_sub(1); } @@ -496,9 +495,9 @@ impl TypeChecker<'_, M> { } if self.def_eq_trace_depth > 0 { - eprintln!("[deq tier4 break] depth={}", self.def_eq_depth); - eprintln!(" wa: {wa}"); - eprintln!(" wb: {wb}"); + log::info!("[deq tier4 break] depth={}", self.def_eq_depth); + log::info!(" wa: {wa}"); + log::info!(" wb: {wb}"); } // Tier 4b: post-delta congruence checks (lean4lean isDefEqConst/Fvar/Proj) @@ -539,15 +538,15 @@ impl TypeChecker<'_, M> { // Tier 5 final-fail trace: when IX_DEF_EQ_TIER5_DUMP is set and the // pair's head names contain the configured substring, dump the // post-whnfCore wa/wb. This is where lazy-delta + Tier 4c gave up. - if let Ok(prefix) = std::env::var("IX_DEF_EQ_TIER5_DUMP") + if let Ok(prefix) = crate::env_var("IX_DEF_EQ_TIER5_DUMP") && let Ok(false) = result.as_ref() { let a_match = head_const_name(&wa).is_some_and(|n| n.contains(&prefix)); let b_match = head_const_name(&wb).is_some_and(|n| n.contains(&prefix)); if prefix.is_empty() || a_match || b_match { - eprintln!("[deq tier5 fail] depth={}", self.def_eq_depth); - eprintln!(" wa: {wa}"); - eprintln!(" wb: {wb}"); + log::info!("[deq tier5 fail] depth={}", self.def_eq_depth); + log::info!(" wa: {wa}"); + log::info!(" wb: {wb}"); } } @@ -807,7 +806,7 @@ impl TypeChecker<'_, M> { Ok(ty) => ty, Err(_) => return Ok(false), }; - if !self.is_prop_type(&a_ty)? { + if !self.is_prop_type(&a_ty) { return Ok(false); } let b_ty = match self.with_infer_only(|tc| tc.infer(b)) { @@ -826,14 +825,11 @@ impl TypeChecker<'_, M> { /// the inner chain are propagated as `Ok(false)` (treating ill-typed /// metadata as non-prop), matching the previous behaviour of /// `try_proof_irrel`. - pub(crate) fn is_prop_type( - &mut self, - ty: &KExpr, - ) -> Result> { + pub(crate) fn is_prop_type(&mut self, ty: &KExpr) -> bool { let cache_key = (ty.hash_key(), self.ctx_addr_for_lbr(ty.lbr())); if let Some(&cached) = self.env.is_prop_cache.get(&cache_key) { self.env.perf.record_is_prop_hit(); - return Ok(cached); + return cached; } self.env.perf.record_is_prop_miss(); self.record_hot_miss("is-prop", ty); @@ -852,7 +848,7 @@ impl TypeChecker<'_, M> { Err(_) => false, }; self.env.is_prop_cache.insert(cache_key, result); - Ok(result) + result } /// Unit-like type: non-recursive, 0 indices, 1 ctor with 0 fields. @@ -933,8 +929,8 @@ impl TypeChecker<'_, M> { if v.0 == num_bigint::BigUint::ZERO { return None; } - let pred = lean_ffi::nat::Nat(&v.0 - num_bigint::BigUint::from(1u64)); - let pred_addr = crate::ix::address::Address::hash(&pred.to_le_bytes()); + let pred = bignat::Nat(&v.0 - num_bigint::BigUint::from(1u64)); + let pred_addr = ix_common::address::Address::hash(&pred.to_le_bytes()); Some(self.env.intern.intern_expr(KExpr::nat(pred, pred_addr))) }, ExprData::App(f, arg, _) => match f.data() { @@ -1047,8 +1043,8 @@ impl TypeChecker<'_, M> { // Build list right-to-left: foldr let mut list = nil; for c in s.chars().rev() { - let nat_val = lean_ffi::nat::Nat::from(c as u64); - let nat_addr = crate::ix::address::Address::hash(&nat_val.to_le_bytes()); + let nat_val = bignat::Nat::from(c as u64); + let nat_addr = ix_common::address::Address::hash(&nat_val.to_le_bytes()); let nat_lit = self.intern(KExpr::nat(nat_val, nat_addr)); let char_val = self.intern(KExpr::app(char_of_nat.clone(), nat_lit)); let partial = self.intern(KExpr::app(cons.clone(), char_val)); @@ -1093,7 +1089,7 @@ impl TypeChecker<'_, M> { // Wrap s as λ(ty). s #0 let s_lifted = lift(&mut self.env.intern, s, 1, 0); let v0 = - self.intern(KExpr::var(0, M::meta_field(crate::ix::env::Name::anon()))); + self.intern(KExpr::var(0, M::meta_field(ix_common::env::Name::anon()))); let body = self.intern(KExpr::app(s_lifted, v0)); let s_lam = self.intern(KExpr::lam(name, bi, ty, body)); self.is_def_eq(t, &s_lam) @@ -1307,7 +1303,7 @@ impl TypeChecker<'_, M> { /// Check if a constant has Regular reducibility hints (not Abbrev or Opaque). /// Used to guard the same-head-spine optimization (lean4lean: dt.hints.isRegular). fn is_regular(&mut self, id: &KId) -> Result> { - use crate::ix::env::ReducibilityHints; + use ix_common::env::ReducibilityHints; Ok(matches!( self.try_get_const(id)?, Some(KConst::Defn { hints: ReducibilityHints::Regular(_), .. }) @@ -1329,7 +1325,7 @@ impl TypeChecker<'_, M> { /// - `Regular(h)` → `(1, h)` (ordered by height within the class) /// - `Abbrev` → `(2, 0)` (strictly greater than every `Regular(h)`) fn def_rank_id(&mut self, id: &KId) -> Result<(u8, u32), TcError> { - use crate::ix::env::ReducibilityHints; + use ix_common::env::ReducibilityHints; Ok(match self.try_get_const(id)? { Some(KConst::Defn { kind, hints, .. }) => match kind { DefKind::Opaque | DefKind::Theorem => (0, 0), @@ -1522,7 +1518,7 @@ impl TypeChecker<'_, M> { if !filter.is_empty() && !id_s.contains(filter) { return; } - eprintln!( + log::info!( "[proj-delta] const={} depth={} phase={} proj={}.{} a={} b={}", self.debug_label.as_deref().unwrap_or(""), self.def_eq_depth, @@ -1566,7 +1562,7 @@ impl TypeChecker<'_, M> { if !filter.is_empty() && !id_s.contains(filter) { return; } - eprintln!( + log::info!( "[eta] const={} depth={} reason={} id={} idx={} a={} b={}", self.debug_label.as_deref().unwrap_or(""), self.def_eq_depth, @@ -1696,17 +1692,21 @@ impl TypeChecker<'_, M> { { return; } - eprintln!( + log::info!( "[deq max] {kind} depth={} a_head={} b_head={} wa_head={} wb_head={}", - self.def_eq_depth, a_head, b_head, wa_head, wb_head + self.def_eq_depth, + a_head, + b_head, + wa_head, + wb_head ); - eprintln!(" a: {a}"); - eprintln!(" b: {b}"); + log::info!(" a: {a}"); + log::info!(" b: {b}"); if let Some(wa) = wa { - eprintln!(" wa: {wa}"); + log::info!(" wa: {wa}"); } if let Some(wb) = wb { - eprintln!(" wb: {wb}"); + log::info!(" wb: {wb}"); } } @@ -1725,7 +1725,7 @@ impl TypeChecker<'_, M> { { return; } - eprintln!( + log::info!( "[deq max] rec-fuel depth={} a={} b={}", self.def_eq_depth, compact_def_eq_expr(a), @@ -1744,9 +1744,9 @@ mod tests { use super::super::level::KUniv; use super::super::mode::{Anon, Meta}; use super::super::tc::TypeChecker; - use crate::ix::address::Address; - use crate::ix::env::{DataValue, DefinitionSafety, Name, ReducibilityHints}; - use crate::ix::ixon::constant::DefKind; + use ix_common::address::Address; + use ix_common::env::{DataValue, DefinitionSafety, Name, ReducibilityHints}; + use ixon::constant::DefKind; type AE = KExpr; type ME = KExpr; diff --git a/src/ix/kernel/env.rs b/crates/kernel/src/env.rs similarity index 96% rename from src/ix/kernel/env.rs rename to crates/kernel/src/env.rs index 1fdda019e..f93a372e6 100644 --- a/src/ix/kernel/env.rs +++ b/crates/kernel/src/env.rs @@ -11,7 +11,7 @@ use std::collections::BTreeSet; use rustc_hash::{FxHashMap, FxHashSet}; use std::cell::OnceCell; -use crate::ix::address::Address; +use ix_common::address::Address; use super::constant::{KConst, RecRule}; use super::error::TcError; @@ -307,6 +307,14 @@ pub struct KEnv { /// universe args). Mirrors lean4 C++ `m_unfold` cache. Cross-call sharing of /// universe-substituted bodies eliminates O(body) walks on every unfold. pub unfold_cache: FxHashMap>, + /// Memo of `try_reduce_nat_succ_iter` STUCK outcomes: succ-chain args + /// whose base never collapses to a literal. Keyed like `whnf_cache` + /// ((expr_hash, ctx_hash)). The succ-collapse loop runs its inner WHNF + /// in `NatSuccMode::Stuck`, which bypasses the WHNF caches, so without + /// this memo a stuck `Nat.succ^k(x)` chain is re-peeled from every + /// depth it is encountered at — O(k²) fuel for symbolic-plus-literal + /// Nat arithmetic (e.g. `x + 0xC0` in the UTF-8 codec proofs). + pub nat_succ_stuck: FxHashSet<(Addr, Addr)>, /// Ingress cache: LeanExpr → KExpr conversion results. /// Keyed by (expr_hash, param_names_hash) to account for different /// level param bindings producing different KExprs from the same LeanExpr. @@ -358,7 +366,7 @@ pub struct KEnv { /// worker; `None` (the default) has zero overhead. Deliberately preserved /// across `clear`/`clear_releasing_memory` so recording survives scheduled /// block boundaries within a run. - pub profile_sink: Option, + pub profile_sink: Option, } impl Default for KEnv { @@ -375,7 +383,7 @@ impl Drop for KEnv { if super::perf::enabled() { let summary = self.perf.summary(); if !summary.is_empty() { - eprint!("{summary}"); + log::info!("{summary}"); } } } @@ -405,6 +413,7 @@ impl KEnv { def_eq_cheap_cache: FxHashMap::default(), def_eq_failure: FxHashSet::default(), unfold_cache: FxHashMap::default(), + nat_succ_stuck: FxHashSet::default(), ingress_cache: FxHashMap::default(), is_prop_cache: FxHashMap::default(), recursor_cache: FxHashMap::default(), @@ -519,6 +528,7 @@ impl KEnv { self.def_eq_cheap_cache.clear(); self.def_eq_failure.clear(); self.unfold_cache.clear(); + self.nat_succ_stuck.clear(); self.ingress_cache.clear(); self.is_prop_cache.clear(); self.recursor_cache.clear(); @@ -580,6 +590,7 @@ impl KEnv { self.def_eq_cheap_cache = FxHashMap::default(); self.def_eq_failure = FxHashSet::default(); self.unfold_cache = FxHashMap::default(); + self.nat_succ_stuck = FxHashSet::default(); self.ingress_cache = FxHashMap::default(); self.is_prop_cache = FxHashMap::default(); self.recursor_cache = FxHashMap::default(); @@ -610,6 +621,7 @@ impl KEnv { self.def_eq_cheap_cache.clear(); self.def_eq_failure.clear(); self.unfold_cache.clear(); + self.nat_succ_stuck.clear(); self.is_prop_cache.clear(); } } @@ -619,7 +631,7 @@ mod tests { use super::super::mode::Anon; use super::super::primitive::PrimAddrs; use super::*; - use crate::ix::address::Address; + use ix_common::address::Address; fn mk_addr(s: &str) -> Address { Address::hash(s.as_bytes()) diff --git a/src/ix/kernel/equiv.rs b/crates/kernel/src/equiv.rs similarity index 100% rename from src/ix/kernel/equiv.rs rename to crates/kernel/src/equiv.rs diff --git a/src/ix/kernel/error.rs b/crates/kernel/src/error.rs similarity index 99% rename from src/ix/kernel/error.rs rename to crates/kernel/src/error.rs index 0d253436a..44ad4852e 100644 --- a/src/ix/kernel/error.rs +++ b/crates/kernel/src/error.rs @@ -2,7 +2,7 @@ use std::cmp::Ordering; -use crate::ix::address::Address; +use ix_common::address::Address; use super::expr::KExpr; use super::mode::KernelMode; diff --git a/src/ix/kernel/expr.rs b/crates/kernel/src/expr.rs similarity index 98% rename from src/ix/kernel/expr.rs rename to crates/kernel/src/expr.rs index 009bcf820..fe5ce40c1 100644 --- a/src/ix/kernel/expr.rs +++ b/crates/kernel/src/expr.rs @@ -6,12 +6,12 @@ use std::fmt; use std::sync::Arc; -use crate::ix::address::Address; -use crate::ix::env::{ +use bignat::Nat; +use ix_common::address::Address; +use ix_common::env::{ BinderInfo, DataValue, EALL, EAPP, EFVAR, ELAM, ELET, ENAT, EPRJ, EREF, ESORT, ESTR, EVAR, Name, }; -use lean_ffi::nat::Nat; use super::env::Addr; use super::id::KId; @@ -48,7 +48,7 @@ pub struct ExprInfo { } /// Per-`TypeChecker` unique identifier for a free variable. Generated by -/// [`crate::ix::kernel::lctx::NameGenerator`] and embedded into the blake3 +/// [`crate::lctx::NameGenerator`] and embedded into the blake3 /// content hash of [`ExprData::FVar`] nodes, so that two distinct fvars hash /// distinctly. This is the soundness lever that lets cache keys be the /// expression hash alone (no separate local-context key) — see the kernel @@ -69,7 +69,7 @@ pub enum ExprData { /// Free variable: opaque identity from the active local context. /// `FVarId` participates in the content hash; the user-facing `Name` is /// preserved (in Meta mode) for diagnostics. The looked-up type lives in - /// the active [`crate::ix::kernel::lctx::LocalContext`], not on the node. + /// the active [`crate::lctx::LocalContext`], not on the node. FVar(FVarId, M::MField, ExprInfo), Sort(KUniv, ExprInfo), Const(KId, Box<[KUniv]>, ExprInfo), @@ -802,8 +802,8 @@ fn collect_spine(e: &KExpr) -> (KExpr, Vec>) { mod tests { use super::super::mode::{Anon, Meta}; use super::*; - use crate::ix::address::Address; - use crate::ix::env::BinderInfo; + use ix_common::address::Address; + use ix_common::env::BinderInfo; type ME = KExpr; type AE = KExpr; diff --git a/src/ix/kernel/id.rs b/crates/kernel/src/id.rs similarity index 98% rename from src/ix/kernel/id.rs rename to crates/kernel/src/id.rs index 621efdf33..36316984c 100644 --- a/src/ix/kernel/id.rs +++ b/crates/kernel/src/id.rs @@ -1,8 +1,8 @@ use std::fmt; use std::hash::{Hash, Hasher}; -use crate::ix::address::Address; -use crate::ix::env::Name; +use ix_common::address::Address; +use ix_common::env::Name; use super::mode::{KernelMode, MetaDisplay, MetaHash}; diff --git a/src/ix/kernel/inductive.rs b/crates/kernel/src/inductive.rs similarity index 98% rename from src/ix/kernel/inductive.rs rename to crates/kernel/src/inductive.rs index 4cba8c1c1..52e810282 100644 --- a/src/ix/kernel/inductive.rs +++ b/crates/kernel/src/inductive.rs @@ -4,9 +4,7 @@ //! constraints, return types) and generates canonical recursors following //! lean4lean's constructive approach, then compares with provided recursors. -use std::sync::LazyLock; - -use crate::ix::address::Address; +use ix_common::address::Address; use super::constant::KConst; use super::env::{GeneratedRecursor, RecursorAuxOrder}; @@ -23,14 +21,14 @@ use super::tc::{TypeChecker, collect_app_spine, expr_mentions_any_addr}; /// regime or a mutual block with near-identical peers triggers a fresh diff, /// turning a normal compile into a wall of stderr. Set `IX_TYPE_DIFF=1` to /// enable when investigating a specific mismatch. -static IX_TYPE_DIFF: LazyLock = - LazyLock::new(|| std::env::var("IX_TYPE_DIFF").is_ok()); +static IX_TYPE_DIFF: crate::EnvFlag = + crate::EnvFlag::new(|| crate::env_var("IX_TYPE_DIFF").is_ok()); /// Emit nested-aux recursor ordering/selection diagnostics for names whose /// display form starts with the configured prefix. Example: /// `IX_RECURSOR_DUMP=Lean.Doc.Block`. -static IX_RECURSOR_DUMP: LazyLock> = LazyLock::new(|| { - std::env::var("IX_RECURSOR_DUMP").ok().filter(|s| !s.is_empty()) +static IX_RECURSOR_DUMP: crate::EnvString = crate::EnvString::new(|| { + crate::env_var("IX_RECURSOR_DUMP").ok().filter(|s| !s.is_empty()) }); /// A member of the "flat" mutual block used for recursor generation. @@ -496,7 +494,7 @@ impl TypeChecker<'_, M> { n_rec_params: u64, univ_offset: u64, ) -> Result>, TcError> { - let anon = || M::meta_field(crate::ix::env::Name::anon()); + let anon = || M::meta_field(ix_common::env::Name::anon()); let all_block_addrs: Vec
= block_inds.iter().map(|id| id.addr.clone()).collect(); @@ -919,7 +917,7 @@ impl TypeChecker<'_, M> { continue; } - let anon = || M::meta_field(crate::ix::env::Name::anon()); + let anon = || M::meta_field(ix_common::env::Name::anon()); let mut result = self.env.intern.intern_expr(KExpr::cnst( aux_ids[idx].clone(), block_us.to_vec().into_boxed_slice(), @@ -954,8 +952,8 @@ impl TypeChecker<'_, M> { n_block_params: u64, ) -> Result< Vec<( - M::MField, - M::MField, + M::MField, + M::MField, KExpr, )>, TcError, @@ -994,8 +992,8 @@ impl TypeChecker<'_, M> { &mut self, body: KExpr, binders: &[( - M::MField, - M::MField, + M::MField, + M::MField, KExpr, )], ) -> KExpr { @@ -1045,13 +1043,11 @@ impl TypeChecker<'_, M> { aux: &[FlatBlockMember], n_block_params: u64, block_us: &[KUniv], - all0_name: Option<&crate::ix::env::Name>, + all0_name: Option<&ix_common::env::Name>, block_first_id: Option<&KId>, ) -> Result, TcError> { - use crate::ix::env::Name; - use crate::ix::kernel::canonical_check::{ - KMutCtx, sort_kconsts_with_seed_key, - }; + use crate::canonical_check::{KMutCtx, sort_kconsts_with_seed_key}; + use ix_common::env::Name; use rustc_hash::FxHashMap; // Build synthetic Indc + Ctor views for each aux. @@ -1071,7 +1067,7 @@ impl TypeChecker<'_, M> { // the block's first inductive is unavailable, the wrap is empty (a no-op). let block_param_binders: Vec<( M::MField, - M::MField, + M::MField, KExpr, )> = match block_first_id { Some(id) if n_block_params > 0 => { @@ -1309,7 +1305,7 @@ impl TypeChecker<'_, M> { }); if dump_canonical { - eprintln!( + log::info!( "[canonical_aux_order.dump] all0={:?} n_aux={} n_block_params={}", all0_name.map(Name::pretty), pairs.len(), @@ -1317,7 +1313,7 @@ impl TypeChecker<'_, M> { ); for (i, (kid, kconst)) in pairs.iter().enumerate() { let seed = aux_seed_names.get(i).cloned().unwrap_or_else(Name::anon); - eprintln!( + log::info!( " pre-sort[{}] addr={} seed={} member_id_addr={}", i, &kid.addr.hex()[..8], @@ -1325,12 +1321,12 @@ impl TypeChecker<'_, M> { &aux[i].id.addr.hex()[..8] ); if let KConst::Indc { ty, ctors, .. } = kconst { - eprintln!(" indc.ty={ty}"); + log::info!(" indc.ty={ty}"); for (ci, ctor_kid) in ctors.iter().enumerate() { if let Some(KConst::Ctor { ty, .. }) = all_ctor_lookup.get(&ctor_kid.addr) { - eprintln!(" ctor[{ci}].ty={ty}"); + log::info!(" ctor[{ci}].ty={ty}"); } } } @@ -1349,10 +1345,10 @@ impl TypeChecker<'_, M> { )?; if dump_canonical { - eprintln!("[canonical_aux_order.dump] post-sort classes:"); + log::info!("[canonical_aux_order.dump] post-sort classes:"); for (ci, class) in classes.iter().enumerate() { for (mi, (kid, _)) in class.iter().enumerate() { - eprintln!(" class[{ci}][{mi}] addr={}", &kid.addr.hex()[..8]); + log::info!(" class[{ci}][{mi}] addr={}", &kid.addr.hex()[..8]); } } } @@ -1411,7 +1407,7 @@ impl TypeChecker<'_, M> { if !self.recursor_dump_matches_block(block_id, flat) { return; } - eprintln!( + log::info!( "[recursor.dump] {label} flat aux order for {block_id}: originals={} aux={}", n_originals, flat.len().saturating_sub(n_originals) @@ -1419,9 +1415,11 @@ impl TypeChecker<'_, M> { for (aux_i, member) in flat.iter().skip(n_originals).enumerate() { let spec = member.spec_params.iter().map(|e| format!("{e}")).collect::>(); - eprintln!( + log::info!( " aux[{aux_i:2}] id={} own_params={} indices={} spec={spec:?}", - member.id, member.own_params, member.n_indices + member.id, + member.own_params, + member.n_indices ); } } @@ -1532,18 +1530,18 @@ impl TypeChecker<'_, M> { failed_gen_major: Option<&KExpr>, failed_stored_major: Option<&KExpr>, ) { - eprintln!( + log::info!( "[recursor.align] FAIL ind_block={ind_block_id} rec_block={rec_block_id} \ peers={} flat={} rec_ids={} failed_gi={failed_gi}", generated_snapshot.len(), flat.len(), rec_ids.len() ); - eprintln!( + log::info!( " failed gen major: {}", Self::major_domain_signature_text(failed_gen_major) ); - eprintln!( + log::info!( " failed stored major: {}", Self::major_domain_signature_text(failed_stored_major) ); @@ -1573,18 +1571,18 @@ peers={} flat={} rec_ids={} failed_gi={failed_gi}", None => None, }; let mark = if gi == failed_gi { "!!" } else { " " }; - eprintln!( + log::info!( " {mark} peer[{gi:2}] flat.id={} target={}… aux={} ind={}…", flat[gi].id, &target_addr.hex()[..8], flat[gi].is_aux, &gen_rec.ind_addr.hex()[..8] ); - eprintln!( + log::info!( " gen : {}", Self::major_domain_signature_text(gen_major.as_ref()) ); - eprintln!( + log::info!( " sto : {} (rid={})", Self::major_domain_signature_text(stored_major.as_ref()), rid @@ -1603,9 +1601,9 @@ peers={} flat={} rec_ids={} failed_gi={failed_gi}", return Ok(false); } if depth > 80 { - eprintln!("[rule rhs diff] first diff {path}: recursion limit"); - eprintln!(" gen: {lhs}"); - eprintln!(" sto: {rhs}"); + log::info!("[rule rhs diff] first diff {path}: recursion limit"); + log::info!(" gen: {lhs}"); + log::info!(" sto: {rhs}"); return Ok(true); } @@ -1621,9 +1619,9 @@ peers={} flat={} rec_ids={} failed_gi={failed_gi}", ExprData::All(_, _, rty, rbody, _), ) => { if !self.is_def_eq(lty, rty)? { - eprintln!("[rule rhs diff] first diff {path}.dom"); - eprintln!(" gen: {lty}"); - eprintln!(" sto: {rty}"); + log::info!("[rule rhs diff] first diff {path}.dom"); + log::info!(" gen: {lty}"); + log::info!(" sto: {rty}"); return Ok(true); } let saved = self.lctx.len(); @@ -1651,9 +1649,9 @@ peers={} flat={} rec_ids={} failed_gi={failed_gi}", self.dump_rule_rhs_first_diff(la, ra, &format!("{path}.arg"), depth + 1) }, _ => { - eprintln!("[rule rhs diff] first diff {path}"); - eprintln!(" gen: {lw}"); - eprintln!(" sto: {rw}"); + log::info!("[rule rhs diff] first diff {path}"); + log::info!(" gen: {lw}"); + log::info!(" sto: {rw}"); Ok(true) }, } @@ -2028,7 +2026,7 @@ peers={} flat={} rec_ids={} failed_gi={failed_gi}", } for (i, u) in us.iter().enumerate() { let expected = - KUniv::param(i as u64, M::meta_field(crate::ix::env::Name::anon())); + KUniv::param(i as u64, M::meta_field(ix_common::env::Name::anon())); if !univ_eq(u, &expected) { self.lctx.truncate(saved); return Err(TcError::Other(format!( @@ -2258,7 +2256,7 @@ peers={} flat={} rec_ids={} failed_gi={failed_gi}", let is_large = self.is_large_eliminator(&result_level, &ind_infos)?; let univ_offset: u64 = if is_large { 1 } else { 0 }; let elim_level = if is_large { - KUniv::param(0, M::meta_field(crate::ix::env::Name::anon())) + KUniv::param(0, M::meta_field(ix_common::env::Name::anon())) } else { KUniv::zero() }; @@ -2295,7 +2293,7 @@ peers={} flat={} rec_ids={} failed_gi={failed_gi}", block_first_id.as_ref(), )?; if self.recursor_dump_matches_block(block_id, &flat) { - eprintln!("[recursor.dump] canonical_order={canonical_order:?}"); + log::info!("[recursor.dump] canonical_order={canonical_order:?}"); } // Apply the permutation produced by sort_consts: each canonical // class index k maps to one representative aux from the original @@ -2395,7 +2393,7 @@ peers={} flat={} rec_ids={} failed_gi={failed_gi}", let n_motives = flat.len() as u64; let n_minors: u64 = flat.iter().map(|m| m.ctors.len() as u64).sum(); let prefix_skip = n_params + n_motives + n_minors; - eprintln!( + log::info!( "[recursor.dump] generated recursors for {block_id}: count={} prefix_skip={prefix_skip}", generated.len() ); @@ -2405,7 +2403,7 @@ peers={} flat={} rec_ids={} failed_gi={failed_gi}", prefix_skip, &g.ind_addr, )?; - eprintln!( + log::info!( " gen[{gi:2}] ind_addr={} {}", &g.ind_addr.hex()[..8], Self::major_domain_signature_text(major.as_ref()) @@ -2482,8 +2480,8 @@ peers={} flat={} rec_ids={} failed_gi={failed_gi}", elim_level: &KUniv, _univ_offset: u64, ) -> Result, TcError> { - let anon = || M::meta_field(crate::ix::env::Name::anon()); - let bi_default = || M::meta_field(crate::ix::env::BinderInfo::Default); + let anon = || M::meta_field(ix_common::env::Name::anon()); + let bi_default = || M::meta_field(ix_common::env::BinderInfo::Default); // Get inductive type and instantiate with occurrence universe args // (concrete for auxiliaries, same as ind_us for originals). @@ -2616,8 +2614,8 @@ peers={} flat={} rec_ids={} failed_gi={failed_gi}", }, }; let (ctor_ty_raw, _ctor_lvls) = ctor; - let anon = || M::meta_field(crate::ix::env::Name::anon()); - let bi_default = || M::meta_field(crate::ix::env::BinderInfo::Default); + let anon = || M::meta_field(ix_common::env::Name::anon()); + let bi_default = || M::meta_field(ix_common::env::BinderInfo::Default); let saved = self.lctx.len(); // Instantiate ctor type with occurrence universe args (concrete for output). @@ -2829,8 +2827,8 @@ peers={} flat={} rec_ids={} failed_gi={failed_gi}", field_domains: &[KExpr], block_addrs: &[Address], ) -> Result, TcError> { - let anon = || M::meta_field(crate::ix::env::Name::anon()); - let bi_default = || M::meta_field(crate::ix::env::BinderInfo::Default); + let anon = || M::meta_field(ix_common::env::Name::anon()); + let bi_default = || M::meta_field(ix_common::env::BinderInfo::Default); // Lift the field domain from its original depth (minor_saved + field_idx) // to the current depth (minor_saved + n_fields + k). @@ -3043,8 +3041,8 @@ peers={} flat={} rec_ids={} failed_gi={failed_gi}", let n_indices = u64_to_usize::(ind_infos[di].2)?; let block_addrs: Vec
= block_inds.iter().map(|id| id.addr.clone()).collect(); - let anon = || M::meta_field(crate::ix::env::Name::anon()); - let bi_default = || M::meta_field(crate::ix::env::BinderInfo::Default); + let anon = || M::meta_field(ix_common::env::Name::anon()); + let bi_default = || M::meta_field(ix_common::env::BinderInfo::Default); // Collect all binder domains in order: params, motives, minors, indices, major let mut domains: Vec> = Vec::new(); @@ -3212,7 +3210,7 @@ peers={} flat={} rec_ids={} failed_gi={failed_gi}", fn mk_ind_univs(&mut self, ind_lvls: u64, offset: u64) -> Box<[KUniv]> { (0..ind_lvls) .map(|i| { - KUniv::param(i + offset, M::meta_field(crate::ix::env::Name::anon())) + KUniv::param(i + offset, M::meta_field(ix_common::env::Name::anon())) }) .collect::>() .into_iter() @@ -3584,8 +3582,8 @@ re-run with `IX_RECURSOR_DUMP={}` for the full breakdown.", is_large: bool, _univ_offset: u64, ) -> Result, TcError> { - let anon = || M::meta_field(crate::ix::env::Name::anon()); - let bi_default = || M::meta_field(crate::ix::env::BinderInfo::Default); + let anon = || M::meta_field(ix_common::env::Name::anon()); + let bi_default = || M::meta_field(ix_common::env::BinderInfo::Default); let ctor_ty_raw = match self.get_const(ctor_id)? { KConst::Ctor { ty, .. } => ty.clone(), @@ -3852,8 +3850,8 @@ re-run with `IX_RECURSOR_DUMP={}` for the full breakdown.", is_large: bool, dom: &KExpr, ) -> Result, TcError> { - let anon = || M::meta_field(crate::ix::env::Name::anon()); - let bi_default = || M::meta_field(crate::ix::env::BinderInfo::Default); + let anon = || M::meta_field(ix_common::env::Name::anon()); + let bi_default = || M::meta_field(ix_common::env::BinderInfo::Default); let target_n_params = u64_to_usize::(flat[target_bi].own_params)?; @@ -3872,7 +3870,7 @@ re-run with `IX_RECURSOR_DUMP={}` for the full breakdown.", }, }; let rec_lvls: Box<[KUniv]> = (0..peer_rec_lvls) - .map(|i| KUniv::param(i, M::meta_field(crate::ix::env::Name::anon()))) + .map(|i| KUniv::param(i, M::meta_field(ix_common::env::Name::anon()))) .collect(); // Peel foralls from the domain to detect wrapping. @@ -4218,15 +4216,17 @@ re-run with `IX_RECURSOR_DUMP={}` for the full breakdown.", .or_else(|| generated.iter().position(|g| g.ind_addr == ind_id.addr)); if self.recursor_dump_matches_id(id) { - eprintln!( + log::info!( "[recursor.dump] check {} rec_block={} resolved_block={} stored_pos={stored_pos:?} selected_idx={selected_idx:?}", - id, rec_block, resolved_block + id, + rec_block, + resolved_block ); - eprintln!( + log::info!( "[recursor.dump] stored major: {}", Self::major_domain_signature_text(stored_major.as_ref()) ); - eprintln!("[recursor.dump] signature_matches={signature_matches:?}"); + log::info!("[recursor.dump] signature_matches={signature_matches:?}"); for (gi, g) in generated.iter().enumerate() { if g.ind_addr != ind_id.addr { continue; @@ -4236,7 +4236,7 @@ re-run with `IX_RECURSOR_DUMP={}` for the full breakdown.", prefix_skip, &g.ind_addr, )?; - eprintln!( + log::info!( " cand[{gi:2}] {}", Self::major_domain_signature_text(major.as_ref()) ); @@ -4289,11 +4289,11 @@ re-run with `IX_RECURSOR_DUMP={}` for the full breakdown.", } else { "idx/major" }; - eprintln!( + log::info!( "[type diff] binder {bi} ({label}) DIFFERS (p={params} m={motives} min={minors})" ); - eprintln!(" gen: {gd}"); - eprintln!(" sto: {sd}"); + log::info!(" gen: {gd}"); + log::info!(" sto: {sd}"); break; } let _ = self.push_fvar_decl_anon(gd.clone()); @@ -4302,9 +4302,9 @@ re-run with `IX_RECURSOR_DUMP={}` for the full breakdown.", bi += 1; }, _ => { - eprintln!("[type diff] return differs at {bi}"); - eprintln!(" gen: {gc}"); - eprintln!(" sto: {sc}"); + log::info!("[type diff] return differs at {bi}"); + log::info!(" gen: {gc}"); + log::info!(" sto: {sc}"); break; }, } @@ -4380,12 +4380,12 @@ re-run with `IX_RECURSOR_DUMP={}` for the full breakdown.", "rhs", 0, ); - eprintln!( + log::info!( "[rule rhs diff] rule {ri} RHS mismatch (fields={})", gen_rule.fields ); - eprintln!(" gen: {}", gen_rule.rhs); - eprintln!(" sto: {}", stored_rule.rhs); + log::info!(" gen: {}", gen_rule.rhs); + log::info!(" sto: {}", stored_rule.rhs); } return Err(TcError::Other(format!( "check_recursor: rule {ri} RHS mismatch" @@ -4505,7 +4505,7 @@ mod tests { use super::super::level::KUniv; use super::super::mode::Anon; use super::super::tc::TypeChecker; - use crate::ix::address::Address; + use ix_common::address::Address; type AE = KExpr; type AU = KUniv; diff --git a/src/ix/kernel/infer.rs b/crates/kernel/src/infer.rs similarity index 92% rename from src/ix/kernel/infer.rs rename to crates/kernel/src/infer.rs index 95f0232ba..e65748eb1 100644 --- a/src/ix/kernel/infer.rs +++ b/crates/kernel/src/infer.rs @@ -1,7 +1,5 @@ //! Type inference. -use std::sync::LazyLock; - use super::constant::KConst; use super::error::{TcError, u64_to_usize}; use super::expr::{ExprData, KExpr}; @@ -18,23 +16,23 @@ use super::tc::{TypeChecker, collect_app_spine}; /// drowning normal `FAIL` lines. Set `IX_APP_DIFF=1` when investigating /// why a specific `a_ty` and `dom` don't match after reduction. Pairs /// with the `a_ty` / `dom` pair already printed by the error display. -static IX_APP_DIFF: LazyLock = - LazyLock::new(|| std::env::var("IX_APP_DIFF").is_ok()); +static IX_APP_DIFF: crate::EnvFlag = + crate::EnvFlag::new(|| crate::env_var("IX_APP_DIFF").is_ok()); /// Dump the full function/type/argument context when App inference fails /// because the inferred function type is not a forall. Off by default: these /// terms can be enormous in mathlib and hide the constant-level failure line. /// Set `IX_INFER_APP_FORALL_DUMP=1`, optionally with /// `IX_KERNEL_DEBUG_CONST=`, for targeted debugging. -static IX_INFER_APP_FORALL_DUMP: LazyLock = - LazyLock::new(|| std::env::var("IX_INFER_APP_FORALL_DUMP").is_ok()); +static IX_INFER_APP_FORALL_DUMP: crate::EnvFlag = + crate::EnvFlag::new(|| crate::env_var("IX_INFER_APP_FORALL_DUMP").is_ok()); /// When set, log every 100K `infer` entries (total, across cache hits /// and real calls). A check using millions of infer calls points to a /// bloated term or a mis-firing cache. Pairs with `IX_DEF_EQ_COUNT_LOG` /// / `IX_WHNF_COUNT_LOG` for a full picture of per-check hotspots. -static IX_INFER_COUNT_LOG: LazyLock = - LazyLock::new(|| std::env::var("IX_INFER_COUNT_LOG").is_ok()); +static IX_INFER_COUNT_LOG: crate::EnvFlag = + crate::EnvFlag::new(|| crate::env_var("IX_INFER_COUNT_LOG").is_ok()); static INFER_COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); @@ -44,7 +42,7 @@ impl TypeChecker<'_, M> { if *IX_INFER_COUNT_LOG { let n = INFER_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed); if n.is_multiple_of(100_000) && n > 0 { - eprintln!("[infer] count={n}"); + log::info!("[infer] count={n}"); } } let infer_only = self.infer_only; @@ -113,27 +111,27 @@ impl TypeChecker<'_, M> { let f_ty = self.infer(f)?; let (dom, cod) = self.ensure_forall(&f_ty).inspect_err(|_err| { if *IX_INFER_APP_FORALL_DUMP && self.debug_label_matches_env() { - eprintln!("[infer App] ensure_forall FAILED"); - eprintln!( + log::info!("[infer App] ensure_forall FAILED"); + log::info!( " const: {}", self.debug_label.as_deref().unwrap_or("") ); - eprintln!(" f: {f}"); - eprintln!(" f_ty: {f_ty}"); - eprintln!(" f_ty addr: {:?}", f_ty.addr()); - eprintln!(" a: {a}"); + log::info!(" f: {f}"); + log::info!(" f_ty: {f_ty}"); + log::info!(" f_ty addr: {:?}", f_ty.addr()); + log::info!(" a: {a}"); if let ExprData::App(ff, fa, _) = f.data() { - eprintln!(" ff: {ff}"); - eprintln!(" ff addr: {:?}", ff.addr()); + log::info!(" ff: {ff}"); + log::info!(" ff addr: {:?}", ff.addr()); if let Ok(ff_ty) = self.infer(ff) { - eprintln!(" ff_ty: {ff_ty}"); - eprintln!(" ff_ty addr: {:?}", ff_ty.addr()); + log::info!(" ff_ty: {ff_ty}"); + log::info!(" ff_ty addr: {:?}", ff_ty.addr()); if let Ok((dom2, cod2)) = self.ensure_forall(&ff_ty) { - eprintln!(" ff_ty dom: {dom2}"); - eprintln!(" ff_ty cod: {cod2}"); + log::info!(" ff_ty dom: {dom2}"); + log::info!(" ff_ty cod: {cod2}"); } } - eprintln!(" fa: {fa}"); + log::info!(" fa: {fa}"); } } })?; @@ -156,31 +154,31 @@ impl TypeChecker<'_, M> { // strategy. let a_whnf = self.whnf(&a_ty); let d_whnf = self.whnf(&dom); - let depth = std::env::var("IX_APP_DIFF_DEPTH") + let depth = crate::env_var("IX_APP_DIFF_DEPTH") .ok() .and_then(|s| s.parse::().ok()) .unwrap_or(2); - eprintln!( + log::info!( "[app diff] AppTypeMismatch at depth={}", self.ctx.len() ); - eprintln!(" f: {}", compact_expr(f)); - eprintln!(" a: {}", compact_expr(a)); - eprintln!(" a_ty: {}", compact_expr_deep(&a_ty, depth)); - eprintln!(" dom: {}", compact_expr_deep(&dom, depth)); - eprintln!(" a_ty data: {:?}", a_ty.data()); - eprintln!(" dom data: {:?}", dom.data()); + log::info!(" f: {}", compact_expr(f)); + log::info!(" a: {}", compact_expr(a)); + log::info!(" a_ty: {}", compact_expr_deep(&a_ty, depth)); + log::info!(" dom: {}", compact_expr_deep(&dom, depth)); + log::info!(" a_ty data: {:?}", a_ty.data()); + log::info!(" dom data: {:?}", dom.data()); match &a_whnf { Ok(w) => { - eprintln!(" a_ty whnf: {}", compact_expr_deep(w, depth)) + log::info!(" a_ty whnf: {}", compact_expr_deep(w, depth)) }, - Err(e) => eprintln!(" a_ty whnf: ERR {e}"), + Err(e) => log::info!(" a_ty whnf: ERR {e}"), } match &d_whnf { Ok(w) => { - eprintln!(" dom whnf: {}", compact_expr_deep(w, depth)) + log::info!(" dom whnf: {}", compact_expr_deep(w, depth)) }, - Err(e) => eprintln!(" dom whnf: ERR {e}"), + Err(e) => log::info!(" dom whnf: ERR {e}"), } } return Err(TcError::AppTypeMismatch { @@ -229,8 +227,8 @@ impl TypeChecker<'_, M> { abstract_fvars(&mut self.env.intern, &body_ty, &[fv_id]); self.lctx.truncate(saved); self.intern(KExpr::all( - M::meta_field(crate::ix::env::Name::anon()), - M::meta_field(crate::ix::env::BinderInfo::Default), + M::meta_field(ix_common::env::Name::anon()), + M::meta_field(ix_common::env::BinderInfo::Default), ty.clone(), abstracted, )) @@ -242,15 +240,15 @@ impl TypeChecker<'_, M> { let saved = self.lctx.len(); let fv_id = self.fresh_fvar_id(); let fv = self.intern(KExpr::fvar(fv_id, name.clone())); - if std::env::var("IX_FVAR_TRACE").is_ok() { - eprintln!( + if crate::env_var("IX_FVAR_TRACE").is_ok() { + log::info!( "[fvar All push] fv={fv_id} ty.addr={:?} ty.lbr={} ctx_len_before_push={} body.lbr={}", ty.addr(), ty.lbr(), self.ctx.len(), body.lbr(), ); - eprintln!(" ty data: {:?}", ty.data()); + log::info!(" ty data: {:?}", ty.data()); } self.lctx.push( fv_id, @@ -316,8 +314,8 @@ impl TypeChecker<'_, M> { self.infer_proj(&struct_id, *field, val, &val_ty)? }, - ExprData::Nat(..) => self.infer_nat_type()?, - ExprData::Str(..) => self.infer_str_type()?, + ExprData::Nat(..) => self.infer_nat_type(), + ExprData::Str(..) => self.infer_str_type(), }; if !infer_only { @@ -477,12 +475,12 @@ impl TypeChecker<'_, M> { } } - fn infer_nat_type(&mut self) -> Result, TcError> { - Ok(self.intern(KExpr::cnst(self.prims.nat.clone(), Box::new([])))) + fn infer_nat_type(&mut self) -> KExpr { + self.intern(KExpr::cnst(self.prims.nat.clone(), Box::new([]))) } - fn infer_str_type(&mut self) -> Result, TcError> { - Ok(self.intern(KExpr::cnst(self.prims.string.clone(), Box::new([])))) + fn infer_str_type(&mut self) -> KExpr { + self.intern(KExpr::cnst(self.prims.string.clone(), Box::new([]))) } fn inductive_app_is_prop( @@ -614,10 +612,10 @@ mod tests { use super::super::level::KUniv; use super::super::mode::Anon; use super::super::tc::TypeChecker; - use crate::ix::address::Address; - use crate::ix::env::{DefinitionSafety, ReducibilityHints}; - use crate::ix::ixon::constant::DefKind; - use lean_ffi::nat::Nat; + use bignat::Nat; + use ix_common::address::Address; + use ix_common::env::{DefinitionSafety, ReducibilityHints}; + use ixon::constant::DefKind; type AE = KExpr; type AU = KUniv; diff --git a/src/ix/kernel/ingress.rs b/crates/kernel/src/ingress.rs similarity index 93% rename from src/ix/kernel/ingress.rs rename to crates/kernel/src/ingress.rs index 38bedb6e2..0b2a4e831 100644 --- a/src/ix/kernel/ingress.rs +++ b/crates/kernel/src/ingress.rs @@ -6,10 +6,12 @@ //! to avoid stack overflow on deeply nested expressions. use std::cell::Cell; +#[cfg(not(target_arch = "riscv64"))] use std::hash::{BuildHasher, Hash}; use std::sync::Arc; use std::time::{Duration, Instant}; +#[cfg(not(target_arch = "riscv64"))] use rayon::iter::{ IntoParallelIterator, IntoParallelRefIterator, ParallelIterator, }; @@ -17,22 +19,27 @@ use rustc_hash::{FxHashMap, FxHashSet}; use dashmap::DashMap; -use crate::ix::address::Address; -use crate::ix::env::{ - BinderInfo, ConstantInfo as LeanCI, DefinitionSafety, Env as LeanEnv, Name, - ReducibilityHints, +use crate::env::Addr; +use bignat::Nat; +use ix_common::address::Address; +#[cfg(not(target_arch = "riscv64"))] +use ix_common::env::ConstantInfo as LeanCI; +#[cfg(not(target_arch = "riscv64"))] +use ix_common::env::DefinitionSafety; +#[cfg(not(target_arch = "riscv64"))] +use ix_common::env::Env as LeanEnv; +use ix_common::env::{BinderInfo, Name, ReducibilityHints}; +#[cfg(not(target_arch = "riscv64"))] +use ixon::constant::DefKind; +use ixon::constant::{ + Constant, ConstantInfo as IxonCI, MutConst as IxonMutConst, }; -use crate::ix::ixon::constant::{ - Constant, ConstantInfo as IxonCI, DefKind, MutConst as IxonMutConst, -}; -use crate::ix::ixon::env::Env as IxonEnv; -use crate::ix::ixon::expr::Expr as IxonExpr; -use crate::ix::ixon::metadata::{ +use ixon::env::Env as IxonEnv; +use ixon::expr::Expr as IxonExpr; +use ixon::metadata::{ ConstantMeta, ConstantMetaInfo, ExprMeta, ExprMetaData, resolve_kvmap, }; -use crate::ix::ixon::univ::Univ as IxonUniv; -use crate::ix::kernel::env::Addr; -use lean_ffi::nat::Nat; +use ixon::univ::Univ as IxonUniv; use super::constant::{KConst, RecRule}; use super::env::{InternTable, KEnv}; @@ -1372,7 +1379,7 @@ fn ingress_expr( #[allow(clippy::too_many_arguments)] fn ingress_defn( - def: &crate::ix::ixon::constant::Definition, + def: &ixon::constant::Definition, self_id: KId, meta: &ConstantMeta, ixon_env: &IxonEnv, @@ -1492,7 +1499,7 @@ fn ingress_defn( #[allow(clippy::too_many_arguments)] fn ingress_recursor( - rec: &crate::ix::ixon::constant::Recursor, + rec: &ixon::constant::Recursor, self_id: KId, meta: &ConstantMeta, ixon_env: &IxonEnv, @@ -1778,7 +1785,7 @@ fn ingress_standalone( #[allow(clippy::too_many_arguments)] fn ingress_muts_inductive( - ind: &crate::ix::ixon::constant::Inductive, + ind: &ixon::constant::Inductive, self_id: &KId, meta: &ConstantMeta, ixon_env: &IxonEnv, @@ -2085,9 +2092,11 @@ fn ingress_muts_block( let resolve_ctor = |cid: &KId| -> Option> { results_ref.iter().find(|(rid, _)| rid == cid).map(|(_, c)| c.clone()) }; - crate::ix::kernel::canonical_check::validate_canonical_block_single_pass::< - M, - >(entry_addr, &indcs, &resolve_ctor) + crate::canonical_check::validate_canonical_block_single_pass::( + entry_addr, + &indcs, + &resolve_ctor, + ) .map_err(|e| format!("{e}"))?; } @@ -2098,7 +2107,7 @@ fn ingress_muts_block( // Lightweight LeanExpr → KExpr ingress (compile-side) // ============================================================================ -use crate::ix::env::{ +use ix_common::env::{ Expr as LeanExpr, ExprData as LeanExprData, Level, LevelData, }; @@ -2483,7 +2492,7 @@ fn lean_expr_to_zexpr_raw( KExpr::prj_mdata(zid, idx.to_u64().unwrap_or(0), e_k, mdata_layers) }, LeanExprData::Lit(lit, _) => { - use crate::ix::env::Literal; + use ix_common::env::Literal; match lit { Literal::NatVal(n) => { // Address must match the Ixon-side blob address for this Nat, @@ -2659,6 +2668,7 @@ pub fn ingress_compiled_names( /// Returns `None` for variants without a mutual block (Axio, Quot, Ctor, Rec). /// Ctors/Recs have their own `induct`/`all` but the block identity comes /// from the inductive, which is what's on the map anyway. +#[cfg(not(target_arch = "riscv64"))] fn lean_constant_all(ci: &LeanCI) -> Option<&Vec> { match ci { LeanCI::DefnInfo(v) => Some(&v.all), @@ -2672,6 +2682,7 @@ fn lean_constant_all(ci: &LeanCI) -> Option<&Vec> { /// Look up position of `name` in its mutual `all` list, returning 0 for /// non-mutuals or constants not found in their own `all`. +#[cfg(not(target_arch = "riscv64"))] fn lean_member_idx(name: &Name, all: Option<&Vec>) -> u64 { all.and_then(|a| a.iter().position(|n| n == name)).map_or(0, |i| i as u64) } @@ -2691,6 +2702,7 @@ fn lean_member_idx(name: &Name, all: Option<&Vec>) -> u64 { /// (dangling refs, partial envs) fall through to `lean_name_to_addr` as a /// best-effort — those cases produce mismatched addresses and will surface /// as `UnknownConst` in the type checker rather than silently succeeding. +#[cfg(not(target_arch = "riscv64"))] pub fn build_leon_addr_map(lean_env: &LeanEnv) -> DashMap { // Build in parallel. Each shard's write lock is contended only when // distinct names happen to hash into the same shard — with 64 default @@ -2719,6 +2731,7 @@ pub fn build_leon_addr_map(lean_env: &LeanEnv) -> DashMap { /// well-formed Lean env should never trigger it. Callers that need /// strict resolution (e.g. "does this name exist?") should check /// `n2a.contains_key` directly. +#[cfg(not(target_arch = "riscv64"))] fn leon_addr_of(name: &Name, n2a: &DashMap) -> Address { n2a.get(name).map_or_else(|| lean_name_to_addr(name), |e| e.value().clone()) } @@ -2726,6 +2739,7 @@ fn leon_addr_of(name: &Name, n2a: &DashMap) -> Address { /// Build the `block` KId for a constant's mutual block. For singletons /// (no `all` or `all` length 1), the block id is the constant's own KId. /// For mutuals, it's the representative (first name in `all`). +#[cfg(not(target_arch = "riscv64"))] fn lean_block_id( self_name: &Name, all: Option<&Vec>, @@ -2736,6 +2750,7 @@ fn lean_block_id( } /// Build the `lean_all` KId list in Meta mode. +#[cfg(not(target_arch = "riscv64"))] fn lean_all_ids(all: &[Name], n2a: &DashMap) -> Vec> { all.iter().map(|n| KId::new(leon_addr_of(n, n2a), n.clone())).collect() } @@ -2744,6 +2759,7 @@ fn lean_all_ids(all: &[Name], n2a: &DashMap) -> Vec> { /// `lean_expr_to_zexpr_with_kenv` with the `n2a` map so inner `Const` /// references resolve to LEON addresses (same scheme used for the KId /// addresses in this constant's own fields). +#[cfg(not(target_arch = "riscv64"))] fn lean_const_to_kconst( self_name: &Name, ci: &LeanCI, @@ -2754,7 +2770,7 @@ fn lean_const_to_kconst( // LEON addressing so `Const` refs inside expressions resolve to the same // addresses we're using for KId keys — any KId we construct here and any // Const-ref we ingress agree on where they point. - let mut expr_to_k = |e: &crate::ix::env::Expr, pn: &[Name]| -> KExpr { + let mut expr_to_k = |e: &ix_common::env::Expr, pn: &[Name]| -> KExpr { lean_expr_to_zexpr_with_kenv(e, pn, kenv, Some(n2a), None) }; @@ -2937,9 +2953,10 @@ fn lean_const_to_kconst( /// **Meta-only**: the existing `lean_expr_to_zexpr_*` family is Meta-mode /// only, so this helper is Meta-mode only by extension. Generalizing to /// `Anon` would require generalizing `lean_expr_to_zexpr_raw` too. +#[cfg(not(target_arch = "riscv64"))] pub fn lean_ingress(lean_env: &LeanEnv) -> KEnv { use std::time::Instant; - let quiet = std::env::var("IX_QUIET").is_ok(); + let quiet = crate::env_var("IX_QUIET").is_ok(); let mut kenv = KEnv::::new_with_recursor_aux_order( super::env::RecursorAuxOrder::Source, ); @@ -2952,7 +2969,7 @@ pub fn lean_ingress(lean_env: &LeanEnv) -> KEnv { let t = Instant::now(); let n2a = build_leon_addr_map(lean_env); if !quiet { - eprintln!( + log::info!( "[lean_ingress] build_leon_addr_map: {:.2}s ({} names)", t.elapsed().as_secs_f32(), n2a.len() @@ -2967,7 +2984,7 @@ pub fn lean_ingress(lean_env: &LeanEnv) -> KEnv { kenv.insert(kid, kc); } if !quiet { - eprintln!( + log::info!( "[lean_ingress] pass 1 (serial ingress): {:.2}s", t.elapsed().as_secs_f32() ); @@ -3048,7 +3065,7 @@ pub fn lean_ingress(lean_env: &LeanEnv) -> KEnv { kenv.blocks.insert(block_id, members); } if !quiet { - eprintln!( + log::info!( "[lean_ingress] phase A (block seed): {:.2}s", t.elapsed().as_secs_f32() ); @@ -3082,7 +3099,7 @@ pub fn lean_ingress(lean_env: &LeanEnv) -> KEnv { } } if !quiet { - eprintln!( + log::info!( "[lean_ingress] phase B (ctor/rec append): {:.2}s", t.elapsed().as_secs_f32() ); @@ -3097,8 +3114,7 @@ pub fn lean_ingress(lean_env: &LeanEnv) -> KEnv { // Returns `Err` only if `prims()` has already been called on this // KEnv — fresh `KEnv::new()` above guarantees that hasn't happened, // so we ignore the Result. - let _ = kenv - .set_prims(crate::ix::kernel::primitive::Primitives::from_env_orig(&kenv)); + let _ = kenv.set_prims(crate::primitive::Primitives::from_env_orig(&kenv)); kenv } @@ -3458,6 +3474,7 @@ fn timed_drop_ns(value: T) -> u64 { /// (atomic refcount; the last decrementer destroys exactly once), and none /// of the value types have custom `Drop` impls — so this is a pure /// parallelisation of the existing teardown. +#[cfg(not(target_arch = "riscv64"))] fn timed_drop_dashmap_par(map: DashMap) -> u64 where K: Eq + Hash + Send, @@ -3475,6 +3492,7 @@ where /// a `Vec<(K, V)>` first (a cheap O(n) sequential pass that just moves owned /// pairs) and then `into_par_iter().for_each(drop)` on the Vec, letting /// rayon distribute the actual destructor work. +#[cfg(not(target_arch = "riscv64"))] fn timed_drop_fxmap_par(map: FxHashMap) -> u64 { let start = Instant::now(); let entries: Vec<(K, V)> = map.into_iter().collect(); @@ -3484,14 +3502,30 @@ fn timed_drop_fxmap_par(map: FxHashMap) -> u64 { /// Opt-out for the parallel drop path: set `IX_SEQ_IXON_DROP=1` to fall back /// to single-threaded `drop` for measurement comparisons. +#[cfg(not(target_arch = "riscv64"))] fn seq_ixon_drop_enabled() -> bool { - std::env::var_os("IX_SEQ_IXON_DROP").is_some() + crate::env_var_os("IX_SEQ_IXON_DROP").is_some() } +#[cfg(not(target_arch = "riscv64"))] +fn ingress_convert_stats_enabled() -> bool { + crate::env_var_os("IX_INGRESS_CONVERT_STATS").is_some() +} +#[cfg(target_arch = "riscv64")] fn ingress_convert_stats_enabled() -> bool { - std::env::var_os("IX_INGRESS_CONVERT_STATS").is_some() + false +} + +#[cfg(target_arch = "riscv64")] +fn drop_ingress_lookups( + _names: FxHashMap, + _name_to_addr: FxHashMap, + _quiet: bool, +) { + // Auto-dropped at end of scope. } +#[cfg(not(target_arch = "riscv64"))] fn drop_ingress_lookups( names: FxHashMap, name_to_addr: FxHashMap, @@ -3500,7 +3534,10 @@ fn drop_ingress_lookups( let total_start = Instant::now(); let names_len = names.len(); let name_to_addr_len = name_to_addr.len(); + #[cfg(not(target_arch = "riscv64"))] let sequential = seq_ixon_drop_enabled(); + #[cfg(target_arch = "riscv64")] + let sequential = true; // Drop the two lookup tables in series; each one fully utilises the rayon // pool internally via `timed_drop_fxmap_par`. Running them in parallel via @@ -3512,20 +3549,29 @@ fn drop_ingress_lookups( name_to_addr_ns: timed_drop_ns(name_to_addr), } } else { - LookupDropTiming { - names_ns: timed_drop_fxmap_par(names), - name_to_addr_ns: timed_drop_fxmap_par(name_to_addr), + #[cfg(not(target_arch = "riscv64"))] + { + LookupDropTiming { + names_ns: timed_drop_fxmap_par(names), + name_to_addr_ns: timed_drop_fxmap_par(name_to_addr), + } } + #[cfg(target_arch = "riscv64")] + unreachable!() }; let total_ns = elapsed_ns(total_start); if !quiet { - eprintln!( + #[cfg(not(target_arch = "riscv64"))] + let threads = rayon::current_num_threads(); + #[cfg(target_arch = "riscv64")] + let threads = 1; + log::info!( "[ixon_ingress] drop lookups: {:.2}s {} threads={} \ (names {:.2}s/{} name_to_addr {:.2}s/{})", seconds(total_ns), if sequential { "sequential" } else { "parallel" }, - rayon::current_num_threads(), + threads, seconds(timing.names_ns), names_len, seconds(timing.name_to_addr_ns), @@ -3540,17 +3586,25 @@ fn insert_standalone_entries( ) -> IngressInsertTiming { let mut timing = IngressInsertTiming::default(); + #[cfg(not(target_arch = "riscv64"))] let phase_start = Instant::now(); for (id, _) in &entries { zenv.blocks.entry(id.clone()).or_default().push(id.clone()); } - timing.blocks_ns = elapsed_ns(phase_start); + #[cfg(not(target_arch = "riscv64"))] + { + timing.blocks_ns = elapsed_ns(phase_start); + } + #[cfg(not(target_arch = "riscv64"))] let phase_start = Instant::now(); for (id, zc) in entries { zenv.insert(id, zc); } - timing.consts_ns = elapsed_ns(phase_start); + #[cfg(not(target_arch = "riscv64"))] + { + timing.consts_ns = elapsed_ns(phase_start); + } timing } @@ -3561,6 +3615,7 @@ fn insert_muts_entries( ) -> IngressInsertTiming { let mut timing = IngressInsertTiming::default(); + #[cfg(not(target_arch = "riscv64"))] let phase_start = Instant::now(); let block_id = entries.first().and_then(|(_, zc)| match zc { KConst::Defn { block, .. } @@ -3573,13 +3628,20 @@ fn insert_muts_entries( if let Some(bid) = block_id { zenv.blocks.insert(bid, member_ids); } - timing.blocks_ns = elapsed_ns(phase_start); + #[cfg(not(target_arch = "riscv64"))] + { + timing.blocks_ns = elapsed_ns(phase_start); + } + #[cfg(not(target_arch = "riscv64"))] let phase_start = Instant::now(); for (id, zc) in entries { zenv.insert(id, zc); } - timing.consts_ns = elapsed_ns(phase_start); + #[cfg(not(target_arch = "riscv64"))] + { + timing.consts_ns = elapsed_ns(phase_start); + } timing } @@ -3599,12 +3661,18 @@ pub fn ixon_ingress( pub fn ixon_ingress_owned( ixon_env: IxonEnv, ) -> Result<(KEnv, InternTable), String> { - let quiet = std::env::var_os("IX_QUIET").is_some(); + let quiet = crate::env_var_os("IX_QUIET").is_some(); let result = ixon_ingress_inner(&ixon_env); drop_ixon_env(ixon_env, quiet); result } +#[cfg(target_arch = "riscv64")] +fn drop_ixon_env(_ixon_env: IxonEnv, _quiet: bool) { + // Auto-dropped at end of scope. +} + +#[cfg(not(target_arch = "riscv64"))] fn drop_ixon_env(ixon_env: IxonEnv, quiet: bool) { let total_start = Instant::now(); // `anon_hints` is a small FxHashMap (one entry per Def from the .ixe's @@ -3623,7 +3691,10 @@ fn drop_ixon_env(ixon_env: IxonEnv, quiet: bool) { // which is single-threaded internally and dominates the total. Doing one // map at a time, fully parallel within, gives clean per-map timing and // saturates the rayon pool on the work that actually matters. + #[cfg(not(target_arch = "riscv64"))] let sequential = seq_ixon_drop_enabled(); + #[cfg(target_arch = "riscv64")] + let sequential = true; let timing = if sequential { IxonDropTiming { consts_ns: timed_drop_ns(consts), @@ -3633,23 +3704,32 @@ fn drop_ixon_env(ixon_env: IxonEnv, quiet: bool) { comms_ns: timed_drop_ns(comms), } } else { - IxonDropTiming { - consts_ns: timed_drop_dashmap_par(consts), - named_ns: timed_drop_dashmap_par(named), - names_ns: timed_drop_dashmap_par(names), - blobs_ns: timed_drop_dashmap_par(blobs), - comms_ns: timed_drop_dashmap_par(comms), + #[cfg(not(target_arch = "riscv64"))] + { + IxonDropTiming { + consts_ns: timed_drop_dashmap_par(consts), + named_ns: timed_drop_dashmap_par(named), + names_ns: timed_drop_dashmap_par(names), + blobs_ns: timed_drop_dashmap_par(blobs), + comms_ns: timed_drop_dashmap_par(comms), + } } + #[cfg(target_arch = "riscv64")] + unreachable!() }; let total_ns = elapsed_ns(total_start); if !quiet { - eprintln!( + #[cfg(not(target_arch = "riscv64"))] + let threads = rayon::current_num_threads(); + #[cfg(target_arch = "riscv64")] + let threads = 1; + log::info!( "[ixon_ingress] drop ixon_env: {:.2}s {} threads={} \ (consts {:.2}s/{} named {:.2}s/{} names {:.2}s/{} blobs {:.2}s/{} comms {:.2}s/{})", seconds(total_ns), if sequential { "sequential" } else { "parallel" }, - rayon::current_num_threads(), + threads, seconds(timing.consts_ns), consts_len, seconds(timing.named_ns), @@ -3667,22 +3747,23 @@ fn drop_ixon_env(ixon_env: IxonEnv, quiet: bool) { fn ixon_ingress_inner( ixon_env: &IxonEnv, ) -> Result<(KEnv, InternTable), String> { - let quiet = std::env::var_os("IX_QUIET").is_some(); + #[cfg(not(target_arch = "riscv64"))] let total_start = Instant::now(); + #[cfg(not(target_arch = "riscv64"))] let phase_start = Instant::now(); validate_no_reserved_marker_addresses(ixon_env)?; - if !quiet { - eprintln!( - "[ixon_ingress] validate_reserved: {:.2}s", - phase_start.elapsed().as_secs_f32() - ); - } + #[cfg(not(target_arch = "riscv64"))] + log::info!( + "[ixon_ingress] validate_reserved: {:.2}s", + phase_start.elapsed().as_secs_f32() + ); let mut intern = InternTable::new(); // Build the address → Lean-name lookup and the Lean-name → projection- // address lookup. See `build_ingress_lookups` for the role each plays. + #[cfg(not(target_arch = "riscv64"))] let phase_start = Instant::now(); let mut names: FxHashMap = FxHashMap::default(); for entry in ixon_env.names.iter() { @@ -3692,18 +3773,18 @@ fn ixon_ingress_inner( for entry in ixon_env.named.iter() { name_to_addr.insert(entry.key().clone(), entry.value().addr.clone()); } - if !quiet { - eprintln!( - "[ixon_ingress] build lookups: {:.2}s ({} names, {} named)", - phase_start.elapsed().as_secs_f32(), - names.len(), - name_to_addr.len() - ); - } + #[cfg(not(target_arch = "riscv64"))] + log::info!( + "[ixon_ingress] build lookups: {:.2}s ({} names, {} named)", + phase_start.elapsed().as_secs_f32(), + names.len(), + name_to_addr.len() + ); // Partition named entries into work items without cloning the `Named` // metadata payloads. Each worker resolves its current Named entry just // before conversion. + #[cfg(not(target_arch = "riscv64"))] let phase_start = Instant::now(); let mut work_items: Vec = Vec::new(); let mut standalone_count = 0usize; @@ -3750,17 +3831,17 @@ fn ixon_ingress_inner( }, } } - if !quiet { - eprintln!( - "[ixon_ingress] partition work: {:.2}s ({} standalone, {} muts)", - phase_start.elapsed().as_secs_f32(), - standalone_count, - muts_count - ); - } + #[cfg(not(target_arch = "riscv64"))] + log::info!( + "[ixon_ingress] partition work: {:.2}s ({} standalone, {} muts)", + phase_start.elapsed().as_secs_f32(), + standalone_count, + muts_count + ); // Convert each standalone constant or Muts block sequentially into the // single-threaded KEnv. + #[cfg(not(target_arch = "riscv64"))] let phase_start = Instant::now(); let convert_stats_enabled = ingress_convert_stats_enabled(); let mut zenv: KEnv = KEnv::new(); @@ -3771,26 +3852,38 @@ fn ixon_ingress_inner( match work_item { IngressWorkItem::Standalone(const_name) => { timing.standalone_items += 1; + #[cfg(not(target_arch = "riscv64"))] let lookup_start = Instant::now(); let named = ixon_env .lookup_name(&const_name) .ok_or_else(|| format!("{const_name}: missing Named entry"))?; - timing.lookup_ns += elapsed_ns(lookup_start); + #[cfg(not(target_arch = "riscv64"))] + { + timing.lookup_ns += elapsed_ns(lookup_start); + } + #[cfg(not(target_arch = "riscv64"))] let const_start = Instant::now(); let constant = match ixon_env.get_const(&named.addr) { Some(c) => { - timing.const_get_ns += elapsed_ns(const_start); + #[cfg(not(target_arch = "riscv64"))] + { + timing.const_get_ns += elapsed_ns(const_start); + } c }, None => { - timing.const_get_ns += elapsed_ns(const_start); + #[cfg(not(target_arch = "riscv64"))] + { + timing.const_get_ns += elapsed_ns(const_start); + } timing.missing_consts += 1; timing.convert_stats = convert_stats; stream = stream.merge(&timing); continue; }, }; + #[cfg(not(target_arch = "riscv64"))] let convert_start = Instant::now(); let entries = ingress_standalone( &const_name, @@ -3804,22 +3897,33 @@ fn ixon_ingress_inner( &mut convert_stats, ) .map_err(|e| format!("{const_name}: {e}"))?; - timing.convert_ns += elapsed_ns(convert_start); + #[cfg(not(target_arch = "riscv64"))] + { + timing.convert_ns += elapsed_ns(convert_start); + } timing.output_consts += entries.len() as u64; + #[cfg(not(target_arch = "riscv64"))] let insert_start = Instant::now(); let insert_timing = insert_standalone_entries(&mut zenv, entries); - timing.insert_ns += elapsed_ns(insert_start); + #[cfg(not(target_arch = "riscv64"))] + { + timing.insert_ns += elapsed_ns(insert_start); + } timing.insert_blocks_ns += insert_timing.blocks_ns; timing.insert_consts_ns += insert_timing.consts_ns; }, IngressWorkItem::Muts(entry_name) => { timing.muts_items += 1; + #[cfg(not(target_arch = "riscv64"))] let lookup_start = Instant::now(); let named = ixon_env .lookup_name(&entry_name) .ok_or_else(|| format!("{entry_name}: missing Named entry"))?; - timing.lookup_ns += elapsed_ns(lookup_start); + #[cfg(not(target_arch = "riscv64"))] + { + timing.lookup_ns += elapsed_ns(lookup_start); + } let all = match &named.meta.info { ConstantMetaInfo::Muts { all, .. } => all, @@ -3829,6 +3933,7 @@ fn ixon_ingress_inner( continue; }, }; + #[cfg(not(target_arch = "riscv64"))] let convert_start = Instant::now(); let entries = ingress_muts_block( &entry_name, @@ -3841,12 +3946,19 @@ fn ixon_ingress_inner( &mut convert_stats, ) .map_err(|e| format!("{entry_name}: {e}"))?; - timing.convert_ns += elapsed_ns(convert_start); + #[cfg(not(target_arch = "riscv64"))] + { + timing.convert_ns += elapsed_ns(convert_start); + } timing.output_consts += entries.len() as u64; + #[cfg(not(target_arch = "riscv64"))] let insert_start = Instant::now(); let insert_timing = insert_muts_entries(&mut zenv, entries); - timing.insert_ns += elapsed_ns(insert_start); + #[cfg(not(target_arch = "riscv64"))] + { + timing.insert_ns += elapsed_ns(insert_start); + } timing.insert_blocks_ns += insert_timing.blocks_ns; timing.insert_consts_ns += insert_timing.consts_ns; }, @@ -3854,107 +3966,110 @@ fn ixon_ingress_inner( timing.convert_stats = convert_stats; stream = stream.merge(&timing); } - if !quiet { - eprintln!( - "[ixon_ingress] stream ingress+insert: {:.2}s", - phase_start.elapsed().as_secs_f32() + #[cfg(not(target_arch = "riscv64"))] + log::info!( + "[ixon_ingress] stream ingress+insert: {:.2}s", + phase_start.elapsed().as_secs_f32() + ); + #[cfg(not(target_arch = "riscv64"))] + log::info!( + "[ixon_ingress] stream detail (worker-sum): lookup {:.2}s, const_get {:.2}s, convert {:.2}s, insert {:.2}s (blocks {:.2}s, consts {:.2}s), work {} standalone/{} muts, output {} consts, missing {}", + seconds(stream.lookup_ns), + seconds(stream.const_get_ns), + seconds(stream.convert_ns), + seconds(stream.insert_ns), + seconds(stream.insert_blocks_ns), + seconds(stream.insert_consts_ns), + stream.standalone_items, + stream.muts_items, + stream.output_consts, + stream.missing_consts + ); + #[cfg(not(target_arch = "riscv64"))] + let cs = &stream.convert_stats; + #[cfg(not(target_arch = "riscv64"))] + if cs.enabled { + let cache_lookups = cs.expr_cache_hits + cs.expr_cache_misses; + log::info!( + "[ixon_ingress] convert cache: roots {} process {} hits {} misses {} hit {:.1}% inserts {} peak {} clears {} cleared {} shares {}", + cs.expr_roots, + cs.expr_process, + cs.expr_cache_hits, + cs.expr_cache_misses, + percent(cs.expr_cache_hits, cache_lookups), + cs.expr_cache_inserts, + cs.expr_cache_peak, + cs.expr_cache_clears, + cs.expr_cache_entries_cleared, + cs.share_expansions ); - eprintln!( - "[ixon_ingress] stream detail (worker-sum): lookup {:.2}s, const_get {:.2}s, convert {:.2}s, insert {:.2}s (blocks {:.2}s, consts {:.2}s), work {} standalone/{} muts, output {} consts, missing {}", - seconds(stream.lookup_ns), - seconds(stream.const_get_ns), - seconds(stream.convert_ns), - seconds(stream.insert_ns), - seconds(stream.insert_blocks_ns), - seconds(stream.insert_consts_ns), - stream.standalone_items, - stream.muts_items, - stream.output_consts, - stream.missing_consts + log::info!( + "[ixon_ingress] convert nodes: sort {} var {} ref {} rec {} app {} lam {} all {} let {} prj {} str {} nat {} callsites {} args {}", + cs.sort_nodes, + cs.var_nodes, + cs.ref_nodes, + cs.rec_nodes, + cs.app_nodes, + cs.lam_nodes, + cs.all_nodes, + cs.let_nodes, + cs.prj_nodes, + cs.str_nodes, + cs.nat_nodes, + cs.callsites, + cs.callsite_args ); - let cs = &stream.convert_stats; - if cs.enabled { - let cache_lookups = cs.expr_cache_hits + cs.expr_cache_misses; - eprintln!( - "[ixon_ingress] convert cache: roots {} process {} hits {} misses {} hit {:.1}% inserts {} peak {} clears {} cleared {} shares {}", - cs.expr_roots, - cs.expr_process, - cs.expr_cache_hits, - cs.expr_cache_misses, - percent(cs.expr_cache_hits, cache_lookups), - cs.expr_cache_inserts, - cs.expr_cache_peak, - cs.expr_cache_clears, - cs.expr_cache_entries_cleared, - cs.share_expansions - ); - eprintln!( - "[ixon_ingress] convert nodes: sort {} var {} ref {} rec {} app {} lam {} all {} let {} prj {} str {} nat {} callsites {} args {}", - cs.sort_nodes, - cs.var_nodes, - cs.ref_nodes, - cs.rec_nodes, - cs.app_nodes, - cs.lam_nodes, - cs.all_nodes, - cs.let_nodes, - cs.prj_nodes, - cs.str_nodes, - cs.nat_nodes, - cs.callsites, - cs.callsite_args - ); - eprintln!( - "[ixon_ingress] convert metadata/univ: mdata_nodes {} mdata_kv_maps {} univ_roots {} univ_cache_hits {} univ_cache_misses {} univ_hit {:.1}% univ_cache_peak {} univ_process {} univ_interns {}", - cs.mdata_nodes, - cs.mdata_kv_maps, - cs.univ_roots, - cs.univ_cache_hits, - cs.univ_cache_misses, - percent(cs.univ_cache_hits, cs.univ_cache_hits + cs.univ_cache_misses), - cs.univ_cache_peak, - cs.univ_process, - cs.univ_interns - ); - let ie_lookups = cs.intern_expr_calls; - let iu_lookups = cs.intern_univ_calls; - eprintln!( - "[ixon_ingress] convert timing (worker-sum): \ - resolve_kvmap {:.2}s/{} arena_walk {:.2}s \ - intern_expr {:.2}s/{} (get_hits {:.1}%) \ - intern_univ {:.2}s/{} (get_hits {:.1}%) \ - expr_cache lookup {:.2}s / insert {:.2}s \ - get_blob {:.2}s/{} \ - kexpr_construct {:.2}s/{} \ - process_arm {:.2}s continuation_arms {:.2}s", - seconds(cs.resolve_kvmap_ns), - cs.resolve_kvmap_calls, - seconds(cs.arena_walk_ns), - seconds(cs.intern_expr_ns), - cs.intern_expr_calls, - percent(cs.intern_expr_get_hits, ie_lookups), - seconds(cs.intern_univ_ns), - cs.intern_univ_calls, - percent(cs.intern_univ_get_hits, iu_lookups), - seconds(cs.expr_cache_lookup_ns), - seconds(cs.expr_cache_insert_ns), - seconds(cs.get_blob_ns), - cs.get_blob_calls, - seconds(cs.kexpr_construct_ns), - cs.kexpr_construct_calls, - seconds(cs.process_arm_ns), - seconds(cs.continuation_arms_ns) - ); - } - eprintln!( - "[ixon_ingress] complete: {:.2}s ({} consts, {} blocks)", - total_start.elapsed().as_secs_f32(), - zenv.len(), - zenv.blocks.len() + log::info!( + "[ixon_ingress] convert metadata/univ: mdata_nodes {} mdata_kv_maps {} univ_roots {} univ_cache_hits {} univ_cache_misses {} univ_hit {:.1}% univ_cache_peak {} univ_process {} univ_interns {}", + cs.mdata_nodes, + cs.mdata_kv_maps, + cs.univ_roots, + cs.univ_cache_hits, + cs.univ_cache_misses, + percent(cs.univ_cache_hits, cs.univ_cache_hits + cs.univ_cache_misses), + cs.univ_cache_peak, + cs.univ_process, + cs.univ_interns + ); + let ie_lookups = cs.intern_expr_calls; + let iu_lookups = cs.intern_univ_calls; + log::info!( + "[ixon_ingress] convert timing (worker-sum): \ + resolve_kvmap {:.2}s/{} arena_walk {:.2}s \ + intern_expr {:.2}s/{} (get_hits {:.1}%) \ + intern_univ {:.2}s/{} (get_hits {:.1}%) \ + expr_cache lookup {:.2}s / insert {:.2}s \ + get_blob {:.2}s/{} \ + kexpr_construct {:.2}s/{} \ + process_arm {:.2}s continuation_arms {:.2}s", + seconds(cs.resolve_kvmap_ns), + cs.resolve_kvmap_calls, + seconds(cs.arena_walk_ns), + seconds(cs.intern_expr_ns), + cs.intern_expr_calls, + percent(cs.intern_expr_get_hits, ie_lookups), + seconds(cs.intern_univ_ns), + cs.intern_univ_calls, + percent(cs.intern_univ_get_hits, iu_lookups), + seconds(cs.expr_cache_lookup_ns), + seconds(cs.expr_cache_insert_ns), + seconds(cs.get_blob_ns), + cs.get_blob_calls, + seconds(cs.kexpr_construct_ns), + cs.kexpr_construct_calls, + seconds(cs.process_arm_ns), + seconds(cs.continuation_arms_ns) ); } + #[cfg(not(target_arch = "riscv64"))] + log::info!( + "[ixon_ingress] complete: {:.2}s ({} consts, {} blocks)", + total_start.elapsed().as_secs_f32(), + zenv.len(), + zenv.blocks.len() + ); - drop_ingress_lookups(names, name_to_addr, quiet); + drop_ingress_lookups(names, name_to_addr, false); Ok((zenv, intern)) } @@ -4014,7 +4129,7 @@ fn validate_no_reserved_marker_addresses( // discards `named`/`names`/`comms` sections. The helpers below do not // depend on those sections being empty — they simply never consult them. -use crate::ix::kernel::mode::Anon; +use crate::mode::Anon; /// Verify that a projection address computed from a block's structure /// is actually present in the env's consts. Wrapped here so the four @@ -4050,27 +4165,27 @@ fn verify_proj_addr_in_env( /// Deterministic IPrj content address for member `idx` of `block`. /// -/// Thin re-export of `crate::ix::ixon::constant::indc_proj_address` — +/// Thin re-export of `ixon::constant::indc_proj_address` — /// the canonical projection-address helper used by both compile and /// ingress paths. Keep the `anon_` alias so existing call sites read /// naturally in the anon-mode pipeline. pub fn anon_indc_proj_addr(block: &Address, idx: u64) -> Address { - crate::ix::ixon::constant::indc_proj_address(idx, block) + ixon::constant::indc_proj_address(idx, block) } /// Deterministic DPrj content address for member `idx` of `block`. pub fn anon_defn_proj_addr(block: &Address, idx: u64) -> Address { - crate::ix::ixon::constant::defn_proj_address(idx, block) + ixon::constant::defn_proj_address(idx, block) } /// Deterministic RPrj content address for member `idx` of `block`. pub fn anon_recr_proj_addr(block: &Address, idx: u64) -> Address { - crate::ix::ixon::constant::recr_proj_address(idx, block) + ixon::constant::recr_proj_address(idx, block) } /// Deterministic CPrj content address for ctor `(idx, cidx)` of `block`. pub fn anon_ctor_proj_addr(block: &Address, idx: u64, cidx: u64) -> Address { - crate::ix::ixon::constant::ctor_proj_address(idx, cidx, block) + ixon::constant::ctor_proj_address(idx, cidx, block) } /// Compute deterministic ctor projection addresses for every constructor of @@ -4078,7 +4193,7 @@ pub fn anon_ctor_proj_addr(block: &Address, idx: u64, cidx: u64) -> Address { fn anon_ctor_addrs( block_addr: &Address, indc_idx: u64, - ind: &crate::ix::ixon::constant::Inductive, + ind: &ixon::constant::Inductive, ) -> Vec
{ (0..ind.ctors.len() as u64) .map(|cidx| anon_ctor_proj_addr(block_addr, indc_idx, cidx)) @@ -4168,7 +4283,7 @@ fn ingress_anon_standalone( /// for the inductive and every ctor. #[allow(clippy::too_many_arguments)] fn ingress_anon_inductive( - ind: &crate::ix::ixon::constant::Inductive, + ind: &ixon::constant::Inductive, self_id: &KId, anon_env: &IxonEnv, block_constant: &Constant, @@ -4488,10 +4603,10 @@ pub fn ingress_anon_addr_shallow( #[cfg(test)] mod tests { use super::*; - use crate::ix::env::{self, BinderInfo}; - use crate::ix::ixon::metadata::CallSiteEntry; - use crate::ix::kernel::expr::ExprData; - use crate::ix::kernel::level::UnivData; + use crate::expr::ExprData; + use crate::level::UnivData; + use ix_common::env::{self, BinderInfo}; + use ixon::metadata::CallSiteEntry; fn mk_name(s: &str) -> Name { let mut n = Name::anon(); @@ -4646,10 +4761,10 @@ mod tests { #[test] fn ixon_ingress_rejects_reserved_marker_named_addr() { let env = IxonEnv::new(); - let marker = crate::ix::kernel::primitive::PrimAddrs::new().eager_reduce; + let marker = crate::primitive::PrimAddrs::new().eager_reduce; env.register_name( mk_name("Evil.marker"), - crate::ix::ixon::env::Named::with_addr(marker), + ixon::env::Named::with_addr(marker), ); let err = match ixon_ingress::(&env) { @@ -4663,15 +4778,13 @@ mod tests { #[test] fn ixon_ingress_rejects_reserved_marker_refs() { let env = IxonEnv::new(); - let marker = crate::ix::kernel::primitive::PrimAddrs::new().eager_reduce; + let marker = crate::primitive::PrimAddrs::new().eager_reduce; let constant = Constant::with_tables( - crate::ix::ixon::constant::ConstantInfo::Axio( - crate::ix::ixon::constant::Axiom { - is_unsafe: false, - lvls: 0, - typ: IxonExpr::sort(0), - }, - ), + ixon::constant::ConstantInfo::Axio(ixon::constant::Axiom { + is_unsafe: false, + lvls: 0, + typ: IxonExpr::sort(0), + }), vec![], vec![marker], vec![], @@ -5062,7 +5175,7 @@ mod tests { let list_addr = Address::hash(b"arbitrary"); ie.named.insert( list_name.clone(), - crate::ix::ixon::env::Named::with_addr(list_addr.clone()), + ixon::env::Named::with_addr(list_addr.clone()), ); let (name_map, addr_map) = build_ingress_lookups(&ie); @@ -5102,7 +5215,7 @@ mod tests { // the synthesized projection — that's the contract the rest of the // anon pipeline depends on (verifying the computed address against // the address actually stored in `env.consts`). - use crate::ix::ixon::constant::{ + use ixon::constant::{ Constant, ConstantInfo, ConstructorProj, DefinitionProj, InductiveProj, RecursorProj, }; diff --git a/src/ix/kernel/lctx.rs b/crates/kernel/src/lctx.rs similarity index 98% rename from src/ix/kernel/lctx.rs rename to crates/kernel/src/lctx.rs index 32c133471..d522e7142 100644 --- a/src/ix/kernel/lctx.rs +++ b/crates/kernel/src/lctx.rs @@ -20,7 +20,7 @@ use rustc_hash::FxHashMap; -use crate::ix::env::{BinderInfo, Name}; +use ix_common::env::{BinderInfo, Name}; use super::env::InternTable; use super::expr::{ExprData, FVarId, KExpr}; @@ -254,8 +254,8 @@ pub fn is_fvar(e: &KExpr) -> bool { #[cfg(test)] mod tests { use super::*; - use crate::ix::kernel::level::KUniv; - use crate::ix::kernel::mode::Anon; + use crate::level::KUniv; + use crate::mode::Anon; type AE = KExpr; type AU = KUniv; diff --git a/src/ix/kernel/level.rs b/crates/kernel/src/level.rs similarity index 99% rename from src/ix/kernel/level.rs rename to crates/kernel/src/level.rs index 58d2b25d8..0ff6df8c0 100644 --- a/src/ix/kernel/level.rs +++ b/crates/kernel/src/level.rs @@ -37,7 +37,7 @@ use std::collections::BTreeMap; use std::fmt; use std::sync::Arc; -use crate::ix::env::{Name, UIMAX, UMAX, UPARAM, USUCC, UZERO}; +use ix_common::env::{Name, UIMAX, UMAX, UPARAM, USUCC, UZERO}; use super::env::Addr; use super::mode::{KernelMode, MetaDisplay}; @@ -708,7 +708,7 @@ pub fn univ_geq(u: &KUniv, v: &KUniv) -> bool { mod tests { use super::super::mode::{Anon, Meta}; use super::*; - use crate::ix::env::Name; + use ix_common::env::Name; type MU = KUniv; type AU = KUniv; diff --git a/crates/kernel/src/lib.rs b/crates/kernel/src/lib.rs new file mode 100644 index 000000000..488c47e91 --- /dev/null +++ b/crates/kernel/src/lib.rs @@ -0,0 +1,173 @@ +// Diagnostic env-var helpers. zkVM std targets (`target_os = "zkvm"` — Zisk, +// SP1, risc0, …) return `Ok` from `std::env::var` for any key (no real env +// in the guest), so every `IX_*` flag fires "on" by default and runs +// expensive diagnostic code paths whose output the guest can't even surface. +// These wrappers short-circuit to "not present" on any zkVM target and +// delegate to the real call elsewhere. +// +// Use these in place of `std::env::var` / `std::env::var_os` everywhere in +// the kernel. The Result / Option chains downstream (`.is_ok()`, `.ok()`, +// `.is_some()`, `.is_err()`, `match`, ...) all work identically. +#[inline] +pub(crate) fn env_var(name: &str) -> Result { + #[cfg(target_os = "zkvm")] + { + let _ = name; + Err(std::env::VarError::NotPresent) + } + #[cfg(not(target_os = "zkvm"))] + { + std::env::var(name) + } +} + +#[inline] +pub(crate) fn env_var_os(name: &str) -> Option { + #[cfg(target_os = "zkvm")] + { + let _ = name; + None + } + #[cfg(not(target_os = "zkvm"))] + { + std::env::var_os(name) + } +} + +// ============================================================================= +// LazyLock replacements for diagnostic env-var statics. +// +// On non-zkVM targets we want the existing thread-safe lazy-init behaviour +// (`std::sync::LazyLock`). On zkVM targets the initialiser would always +// produce a "not set" value (because `env_var` is hard-wired to `Err`), so +// `LazyLock`'s per-access atomic state load is pure overhead. These types +// collapse to a plain `bool` / `Option` field on zkVM and a real +// `LazyLock` everywhere else. Same `*FLAG` / `FLAG.as_ref()` call sites +// either way. +// ============================================================================= + +/// Boolean env-var flag (e.g. `IX_HOT_MISSES`). Compiles to `false` on zkVM. +pub(crate) struct EnvFlag { + #[cfg(not(target_os = "zkvm"))] + inner: std::sync::LazyLock bool>, + #[cfg(target_os = "zkvm")] + inner: bool, +} + +impl EnvFlag { + pub(crate) const fn new(_init: fn() -> bool) -> Self { + Self { + #[cfg(not(target_os = "zkvm"))] + inner: std::sync::LazyLock::new(_init), + #[cfg(target_os = "zkvm")] + inner: false, + } + } +} + +impl std::ops::Deref for EnvFlag { + type Target = bool; + #[inline] + fn deref(&self) -> &bool { + &self.inner + } +} + +/// `Option` env-var flag (e.g. `IX_DELTA_TRACE`). Compiles to +/// `None` on zkVM (so `IX_X.as_ref()` always returns `None` cheaply). +pub(crate) struct EnvString { + #[cfg(not(target_os = "zkvm"))] + inner: std::sync::LazyLock, fn() -> Option>, + #[cfg(target_os = "zkvm")] + inner: Option, +} + +impl EnvString { + pub(crate) const fn new(_init: fn() -> Option) -> Self { + Self { + #[cfg(not(target_os = "zkvm"))] + inner: std::sync::LazyLock::new(_init), + #[cfg(target_os = "zkvm")] + inner: None, + } + } +} + +impl std::ops::Deref for EnvString { + type Target = Option; + #[inline] + fn deref(&self) -> &Option { + &self.inner + } +} + +/// `Option` env-var flag (e.g. `IX_MAX_REC_FUEL`). Compiles to `None` +/// on zkVM. +pub(crate) struct EnvOptU64 { + #[cfg(not(target_os = "zkvm"))] + inner: std::sync::LazyLock, fn() -> Option>, + #[cfg(target_os = "zkvm")] + inner: Option, +} + +impl EnvOptU64 { + pub(crate) const fn new(_init: fn() -> Option) -> Self { + Self { + #[cfg(not(target_os = "zkvm"))] + inner: std::sync::LazyLock::new(_init), + #[cfg(target_os = "zkvm")] + inner: None, + } + } +} + +impl std::ops::Deref for EnvOptU64 { + type Target = Option; + #[inline] + fn deref(&self) -> &Option { + &self.inner + } +} + +pub mod anon_env; +pub mod anon_work; +pub mod canonical_check; +pub mod check; +pub mod claim; +pub mod congruence; +pub mod constant; +pub mod def_eq; +pub mod env; +pub mod equiv; +pub mod error; +pub mod expr; +pub mod id; +pub mod inductive; +pub mod infer; +pub mod ingress; +pub mod lctx; +pub mod level; +pub mod mode; +pub mod perf; +pub mod primitive; +// Sharding cost model + partitioner (out-of-circuit). `profile` records +// per-block heartbeats + the delta-unfold graph (the cost graph); `shard` +// partitions that graph into balanced, low-cross-ingress shards. Ported here +// from jcb/kernel-sharding (was the monolithic `crate::ix::{profile,shard}`): +// `profile` must live in the kernel crate because `KEnv` holds a `ProfileSink`. +// +// `profile` is dependency-light (hash maps only) so it compiles for the zkVM +// guest, where the `ProfileSink` field is simply always `None`. `shard` is the +// host-only partitioner — it uses rayon + `std::time` (gated to the same +// non-riscv targets that supply those deps), and the guest never partitions. +pub mod profile; +#[cfg(not(target_arch = "riscv64"))] +pub mod shard; +pub mod subst; +pub mod tc; +pub mod whnf; + +#[cfg(test)] +pub mod testing; +#[cfg(test)] +mod tutorial; diff --git a/src/ix/kernel/mode.rs b/crates/kernel/src/mode.rs similarity index 99% rename from src/ix/kernel/mode.rs rename to crates/kernel/src/mode.rs index 0477e8f15..cbaf1aa69 100644 --- a/src/ix/kernel/mode.rs +++ b/crates/kernel/src/mode.rs @@ -13,7 +13,7 @@ use std::fmt::{self, Debug}; use std::hash::Hash; -use crate::ix::env::{BinderInfo, DataValue, Name, NameData}; +use ix_common::env::{BinderInfo, DataValue, Name, NameData}; /// Serialize a metadata value into a `blake3::Hasher`. /// The `()` impl is a no-op, so erased metadata contributes nothing. @@ -44,7 +44,7 @@ impl MetaHash for BinderInfo { impl MetaHash for DataValue { fn meta_hash(&self, hasher: &mut blake3::Hasher) { - crate::ix::env::hash_data_value(self, hasher); + ix_common::env::hash_data_value(self, hasher); } } diff --git a/src/ix/kernel/perf.rs b/crates/kernel/src/perf.rs similarity index 86% rename from src/ix/kernel/perf.rs rename to crates/kernel/src/perf.rs index baf04e45c..8f1bcf888 100644 --- a/src/ix/kernel/perf.rs +++ b/crates/kernel/src/perf.rs @@ -27,11 +27,10 @@ //! atomic op. use std::fmt; -use std::sync::LazyLock; use std::sync::atomic::{AtomicU64, Ordering}; -static PERF_ENABLED: LazyLock = - LazyLock::new(|| std::env::var_os("IX_PERF_COUNTERS").is_some()); +static PERF_ENABLED: crate::EnvFlag = + crate::EnvFlag::new(|| crate::env_var_os("IX_PERF_COUNTERS").is_some()); /// Returns `true` iff `IX_PERF_COUNTERS` is set in the environment at the /// time this is first read. The result is cached for the lifetime of the @@ -301,6 +300,56 @@ impl PerfCounters { } } +// -- Reduction histograms (IX_REDUCE_HISTO) -------------------------------- +// +// Per-constant counts of delta unfolds and iota (recursor) reductions, +// process-global so a harness can run a single block and ask "which +// constants did the kernel grind on?". Gated separately from the cache +// counters because the DashMap entry bump is heavier than an atomic add. + +static REDUCE_HISTO_ENABLED: crate::EnvFlag = + crate::EnvFlag::new(|| crate::env_var_os("IX_REDUCE_HISTO").is_some()); + +/// Returns `true` iff `IX_REDUCE_HISTO` is set in the environment. +#[inline] +pub fn reduce_histo_enabled() -> bool { + *REDUCE_HISTO_ENABLED +} + +/// Delta-unfold counts keyed by the unfolded constant's address. +pub static DELTA_HISTO: std::sync::LazyLock< + dashmap::DashMap, +> = std::sync::LazyLock::new(dashmap::DashMap::new); + +/// Iota-reduction counts keyed by the recursor's address. +pub static IOTA_HISTO: std::sync::LazyLock< + dashmap::DashMap, +> = std::sync::LazyLock::new(dashmap::DashMap::new); + +/// Count of `Nat.succ` peels performed by `try_reduce_nat_succ_iter`. +pub static NAT_SUCC_PEELS: AtomicU64 = AtomicU64::new(0); + +#[inline] +pub fn record_delta_histo(addr: &ix_common::address::Address) { + if reduce_histo_enabled() { + *DELTA_HISTO.entry(addr.clone()).or_insert(0) += 1; + } +} + +#[inline] +pub fn record_iota_histo(addr: &ix_common::address::Address) { + if reduce_histo_enabled() { + *IOTA_HISTO.entry(addr.clone()).or_insert(0) += 1; + } +} + +#[inline] +pub fn record_nat_succ_peel() { + if reduce_histo_enabled() { + NAT_SUCC_PEELS.fetch_add(1, Ordering::Relaxed); + } +} + fn write_rate( out: &mut impl fmt::Write, label: &str, diff --git a/src/ix/kernel/primitive.rs b/crates/kernel/src/primitive.rs similarity index 98% rename from src/ix/kernel/primitive.rs rename to crates/kernel/src/primitive.rs index 270ebea60..3ffa1c48b 100644 --- a/src/ix/kernel/primitive.rs +++ b/crates/kernel/src/primitive.rs @@ -15,7 +15,7 @@ use std::sync::LazyLock; -use crate::ix::address::Address; +use ix_common::address::Address; use super::env::KEnv; use super::id::KId; @@ -1006,7 +1006,7 @@ impl Primitives { /// `@` fallbacks. pub fn from_addr_names(mut name_for_addr: F) -> Self where - F: FnMut(&Address) -> Option, + F: FnMut(&Address) -> Option, { Self::from_addrs_with(&PrimAddrs::new(), |addr| { name_for_addr(addr) @@ -1036,16 +1036,16 @@ impl Primitives { let mut r = |addr: &Address| -> KId { resolve(addr).unwrap_or_else(|| { let hex = addr.hex(); - let name = crate::ix::env::Name::str( - crate::ix::env::Name::anon(), + let name = ix_common::env::Name::str( + ix_common::env::Name::anon(), format!("@{}", &hex[..8]), ); KId::new(addr.clone(), M::meta_field(name)) }) }; let marker = |addr: &Address, marker_name: &str| -> KId { - let name = crate::ix::env::Name::str( - crate::ix::env::Name::anon(), + let name = ix_common::env::Name::str( + ix_common::env::Name::anon(), format!("@{marker_name}"), ); KId::new(addr.clone(), M::meta_field(name)) @@ -1158,12 +1158,12 @@ mod tests { use std::collections::HashMap; use super::*; - use crate::ix::env::Name; - use crate::ix::kernel::constant::KConst; - use crate::ix::kernel::expr::KExpr; - use crate::ix::kernel::id::KId; - use crate::ix::kernel::level::KUniv; - use crate::ix::kernel::mode::Anon; + use crate::constant::KConst; + use crate::expr::KExpr; + use crate::id::KId; + use crate::level::KUniv; + use crate::mode::Anon; + use ix_common::env::Name; /// Collect every (field_name, addr) pair from `PrimAddrs` via reflection /// over a macro invocation at the caller — done here by an inline array. @@ -1360,7 +1360,7 @@ mod tests { // With an empty env, every `r(&a.*)` lookup misses and produces a // synthetic `@` KId. Confirm construction succeeds and // yields recognizable synthetic names (in Meta mode). - let env = KEnv::::new(); + let env = KEnv::::new(); let p = Primitives::from_env(&env); // The fallback name is `@`, a string part under an // anonymous Name. Verify the `nat` field lives at the expected @@ -1398,7 +1398,7 @@ mod tests { #[test] fn primitives_from_env_orig_uses_orig_addrs() { // from_env_orig uses PrimAddrs::new_orig (LEON addrs), not new(). - let env = KEnv::::new(); + let env = KEnv::::new(); let p = Primitives::from_env_orig(&env); let orig = PrimAddrs::new_orig(); let canon = PrimAddrs::new(); @@ -1413,7 +1413,7 @@ mod tests { // Check that the synthetic fallback name has the `@<8hex>` shape for // an address that doesn't exist in the env. Uses Meta mode so the // name metadata is observable. - let env = KEnv::::new(); + let env = KEnv::::new(); let p = Primitives::from_env_orig(&env); // Name of `p.nat` should be `@`. let orig = PrimAddrs::new_orig(); diff --git a/src/ix/profile.rs b/crates/kernel/src/profile.rs similarity index 71% rename from src/ix/profile.rs rename to crates/kernel/src/profile.rs index ed05c5399..1cc117908 100644 --- a/src/ix/profile.rs +++ b/crates/kernel/src/profile.rs @@ -1,4 +1,4 @@ -//! Out-of-circuit kernel profile (`.ixesp`) — the cost + delta-graph hints +//! Out-of-circuit kernel profile (`.ixprof`) — the cost + delta-graph hints //! that drive the sharding strategy (see `plans/sharding.md`). //! //! A profile is computed by running the Rust kernel **out of circuit** over an @@ -18,19 +18,105 @@ //! //! The delta graph is stored in compressed-sparse-row (CSR) form keyed by //! stable block ids (assigned by sorting block addresses), and the on-disk -//! `.ixesp` format is an explicit little-endian binary so it does not depend +//! `.ixprof` format is an explicit little-endian binary so it does not depend //! on the optional `serde`/`bincode` feature. // Block ids and counts are `u32` (envs are far below the limit); the binary // decoder maps decode failures to a single message. Both are intentional here. #![allow(clippy::cast_possible_truncation, clippy::map_err_ignore)] +#[cfg(not(target_os = "zkvm"))] +use std::cell::Cell; + use rustc_hash::{FxHashMap, FxHashSet}; -use crate::ix::address::Address; +use ix_common::address::Address; + +// ───────────────────────────────────────────────────────────────────────────── +// Per-constant operation counters (richer cost features than `heartbeats`). +// +// Heartbeats count kernel reduction *steps* but not the SIZE of the term each +// step touches, so they mispredict in-circuit Zisk cycles ~3× for def-eq-dense +// constants. These thread-local counters record the actual work volume — +// substitution-node visits, whnf/def-eq calls — which tracks guest cycles far +// more tightly, and `subst` feeds the planner's per-shard cost model. The +// profiler runs one constant per worker thread, so a thread-local accumulator +// captures one constant's totals with no arg threading through the +// (free-function) hot paths. Always recorded on native targets (every +// `ix profile` run needs them); compiled out entirely on the zkvm guest, which +// never records, so the in-circuit kernel pays nothing. +// ───────────────────────────────────────────────────────────────────────────── + +// thread_local! needs threads — only declared off the (single-threaded, +// no-std-ish) zkvm guest target, where every bump/take below compiles to a +// no-op. +#[cfg(not(target_os = "zkvm"))] +thread_local! { + static SUBST_NODES: Cell = const { Cell::new(0) }; + static WHNF_CALLS: Cell = const { Cell::new(0) }; + static DEF_EQ_CALLS: Cell = const { Cell::new(0) }; + static NAT_ARITH: Cell = const { Cell::new(0) }; +} + +/// Count one substitution-node visit (called per node in `instantiate_rev`). +#[inline(always)] +pub fn bump_subst_nodes() { + #[cfg(not(target_os = "zkvm"))] + SUBST_NODES.with(|c| c.set(c.get().wrapping_add(1))); +} + +/// Count one `whnf` entry. +#[inline(always)] +pub fn bump_whnf() { + #[cfg(not(target_os = "zkvm"))] + WHNF_CALLS.with(|c| c.set(c.get().wrapping_add(1))); +} + +/// Count one `is_def_eq` entry. +#[inline(always)] +pub fn bump_def_eq() { + #[cfg(not(target_os = "zkvm"))] + DEF_EQ_CALLS.with(|c| c.set(c.get().wrapping_add(1))); +} + +/// Add `work` units of big-Nat arithmetic limb-work (called per native Nat +/// binop in `compute_nat_bin`, weighted by operand limb sizes). Tracks the +/// cost of the Aiur `klimbs_*`/`u64_*` arithmetic circuits, which scale with +/// limb count and are invisible to the hb/subst/def_eq counters. +#[inline(always)] +pub fn bump_nat_arith(work: u64) { + #[cfg(not(target_os = "zkvm"))] + NAT_ARITH.with(|c| c.set(c.get().wrapping_add(work))); + #[cfg(target_os = "zkvm")] + let _ = work; +} + +/// Richer per-constant cost features, recorded alongside `fuel`/heartbeats. +#[derive(Default, Debug, Clone, Copy)] +pub struct OpCounts { + pub subst_nodes: u64, + pub whnf_calls: u64, + pub def_eq_calls: u64, + pub nat_arith: u64, +} + +/// Read and reset the thread-local op counters (call at each constant boundary). +pub fn take_op_counts() -> OpCounts { + #[cfg(not(target_os = "zkvm"))] + { + OpCounts { + subst_nodes: SUBST_NODES.with(|c| c.replace(0)), + whnf_calls: WHNF_CALLS.with(|c| c.replace(0)), + def_eq_calls: DEF_EQ_CALLS.with(|c| c.replace(0)), + nat_arith: NAT_ARITH.with(|c| c.replace(0)), + } + } + #[cfg(target_os = "zkvm")] + OpCounts::default() +} -/// Magic bytes at the head of every `.ixesp` file. -const MAGIC: &[u8; 8] = b"IXESP\0\0\0"; +/// Magic bytes at the head of every `.ixprof` file. +const MAGIC: &[u8; 8] = b"IXPROF\0\0"; /// On-disk format version. Bump on any incompatible layout change. const VERSION: u32 = 1; @@ -45,6 +131,10 @@ pub struct BlockEntry { pub serialized_size: u32, /// Number of constants in the block (1 for standalone constants). pub const_count: u32, + /// Substitution-node visits (`instantiate_rev`) checking this block — the + /// dominant reduction-volume cost driver. Recorded when profiled with op + /// counters enabled; 0 otherwise. + pub subst: u64, } /// A recorded kernel profile over an environment. @@ -121,11 +211,11 @@ impl BlockProfile { self.blocks.iter().map(|b| u128::from(b.heartbeats)).sum() } - /// Serialize to the `.ixesp` binary format. + /// Serialize to the `.ixprof` binary format. pub fn to_bytes(&self) -> Vec { let n = self.blocks.len(); let mut out = Vec::with_capacity( - 8 + 4 + 4 + n * 48 + 8 + (n + 1) * 8 + self.delta_col.len() * 4, + 8 + 4 + 4 + n * 56 + 8 + (n + 1) * 8 + self.delta_col.len() * 4, ); out.extend_from_slice(MAGIC); out.extend_from_slice(&VERSION.to_le_bytes()); @@ -135,6 +225,7 @@ impl BlockProfile { out.extend_from_slice(&b.heartbeats.to_le_bytes()); out.extend_from_slice(&b.serialized_size.to_le_bytes()); out.extend_from_slice(&b.const_count.to_le_bytes()); + out.extend_from_slice(&b.subst.to_le_bytes()); } out.extend_from_slice(&(self.delta_col.len() as u64).to_le_bytes()); // CSR row offsets (n+1 entries) as u64. @@ -147,7 +238,7 @@ impl BlockProfile { out } - /// Deserialize from the `.ixesp` binary format. + /// Deserialize from the `.ixprof` binary format. pub fn from_bytes(bytes: &[u8]) -> Result { let mut r = Reader::new(bytes); let magic = r.take(8)?; @@ -166,11 +257,13 @@ impl BlockProfile { let heartbeats = r.u64()?; let serialized_size = r.u32()?; let const_count = r.u32()?; + let subst = r.u64()?; blocks.push(BlockEntry { addr, heartbeats, serialized_size, const_count, + subst, }); } let num_edges = r.u64()? as usize; @@ -203,7 +296,7 @@ impl BlockProfile { } } -/// Errors from decoding a `.ixesp` file. +/// Errors from decoding a `.ixprof` file. #[derive(Debug, Clone, PartialEq, Eq)] pub enum ProfileError { BadMagic, @@ -215,12 +308,12 @@ pub enum ProfileError { impl std::fmt::Display for ProfileError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - ProfileError::BadMagic => write!(f, "not an .ixesp file (bad magic)"), + ProfileError::BadMagic => write!(f, "not an .ixprof file (bad magic)"), ProfileError::BadVersion(v) => { - write!(f, "unsupported .ixesp version {v}") + write!(f, "unsupported .ixprof version {v}") }, - ProfileError::Truncated => write!(f, "truncated .ixesp file"), - ProfileError::Corrupt => write!(f, "corrupt .ixesp file"), + ProfileError::Truncated => write!(f, "truncated .ixprof file"), + ProfileError::Corrupt => write!(f, "corrupt .ixprof file"), } } } @@ -272,6 +365,7 @@ struct Accum { heartbeats: u64, serialized_size: u32, const_count: u32, + subst: u64, producers: FxHashSet
, } @@ -289,11 +383,13 @@ impl ProfileBuilder { heartbeats: u64, serialized_size: u32, const_count: u32, + subst: u64, ) { let e = self.blocks.entry(addr).or_default(); e.heartbeats = e.heartbeats.saturating_add(heartbeats); e.serialized_size = serialized_size; e.const_count = e.const_count.saturating_add(const_count); + e.subst = e.subst.saturating_add(subst); } /// Record that `consumer` delta-unfolds the body of `producer`. Self-edges @@ -329,6 +425,7 @@ impl ProfileBuilder { heartbeats: a.heartbeats, serialized_size: a.serialized_size, const_count: a.const_count, + subst: a.subst, }); let mut prods: Vec = a.producers.iter().map(|p| id_of[p]).collect(); prods.sort_unstable(); @@ -363,6 +460,10 @@ pub struct ConstRecord { pub fuel: u64, /// Constant addresses whose bodies were delta-unfolded during the check. pub producers: FxHashSet
, + /// Richer cost features (substitution-node visits, whnf/def-eq calls), + /// recorded on every native profiling run; compiled out (all zero) on the + /// zkvm target. + pub ops: OpCounts, } impl ProfileSink { @@ -370,17 +471,23 @@ impl ProfileSink { ProfileSink { isolate, records: FxHashMap::default() } } - /// Accumulate one constant's record (additive in fuel, set-union in - /// producers) so repeated flushes for the same constant combine correctly. + /// Accumulate one constant's record (additive in fuel + op counts, set-union + /// in producers) so repeated flushes for the same constant combine correctly. pub fn record( &mut self, consumer: Address, fuel: u64, producers: impl IntoIterator, + ops: OpCounts, ) { let rec = self.records.entry(consumer).or_default(); rec.fuel = rec.fuel.saturating_add(fuel); rec.producers.extend(producers); + rec.ops.subst_nodes = rec.ops.subst_nodes.saturating_add(ops.subst_nodes); + rec.ops.whnf_calls = rec.ops.whnf_calls.saturating_add(ops.whnf_calls); + rec.ops.def_eq_calls = + rec.ops.def_eq_calls.saturating_add(ops.def_eq_calls); + rec.ops.nat_arith = rec.ops.nat_arith.saturating_add(ops.nat_arith); } /// Merge another worker's sink into this one (order-independent). @@ -389,6 +496,11 @@ impl ProfileSink { let e = self.records.entry(addr).or_default(); e.fuel = e.fuel.saturating_add(rec.fuel); e.producers.extend(rec.producers); + e.ops.subst_nodes = e.ops.subst_nodes.saturating_add(rec.ops.subst_nodes); + e.ops.whnf_calls = e.ops.whnf_calls.saturating_add(rec.ops.whnf_calls); + e.ops.def_eq_calls = + e.ops.def_eq_calls.saturating_add(rec.ops.def_eq_calls); + e.ops.nat_arith = e.ops.nat_arith.saturating_add(rec.ops.nat_arith); } } } @@ -404,9 +516,9 @@ mod tests { fn sample() -> BlockProfile { let mut b = ProfileBuilder::new(); // Three blocks a, /// Net weights = serialized_size of the unfolded block. net_weight: Vec, @@ -134,7 +169,7 @@ impl Hypergraph { pub fn from_profile(profile: &BlockProfile) -> Hypergraph { let n = profile.num_blocks(); let vweight: Vec = - (0..n as u32).map(|i| profile.block(i).heartbeats).collect(); + (0..n as u32).map(|i| block_step_cost(profile.block(i))).collect(); let (row, col) = profile.consumers_csr(); let mut net_weight = Vec::new(); let mut net_pins = Vec::new(); @@ -184,9 +219,23 @@ impl Hypergraph { /// the per-bisection balance tolerance (e.g. `0.05` for ±5%). `num_shards` /// need not be a power of two. pub fn partition(&self, num_shards: usize, epsilon: f64) -> Vec { + self.partition_with_tree(num_shards, epsilon).0 + } + + /// Like [`Self::partition`], but also returns the **bisection tree** — the + /// binary tree of min-cut splits whose leaves are the shard ids. Reusing this + /// as the proof-aggregation tree (rather than an arbitrary flat fold) + /// discharges each cross-shard assumption at the lowest common ancestor of the + /// shards that share it; the per-bisection min-cut means sibling subtrees have + /// the thinnest possible interface, so most discharge happens low in the tree. + pub fn partition_with_tree( + &self, + num_shards: usize, + epsilon: f64, + ) -> (Vec, AggNode) { let n = self.num_vertices(); if num_shards <= 1 { - return vec![0u32; n]; // everything in shard 0 + return (vec![0u32; n], AggNode::Leaf(0)); // everything in shard 0 } // Cap each block's balance weight at the ideal per-shard heartbeats // (total / num_shards). This keeps balancing heartbeat-aware while a balanced @@ -202,9 +251,152 @@ impl Hypergraph { // they can be partitioned on separate threads without their writes aliasing. let shard_of: Vec = (0..n).map(|_| AtomicU32::new(0)).collect(); let prog = PartitionProgress::new(num_shards); - rec_bisect(&sub, num_shards, 0, epsilon, &shard_of, &prog); + let tree = rec_bisect(&sub, num_shards, 0, epsilon, &shard_of, &prog); prog.done(); - shard_of.into_iter().map(AtomicU32::into_inner).collect() + let assignment = shard_of.into_iter().map(AtomicU32::into_inner).collect(); + (assignment, tree) + } +} + +// ============================================================================ +// Bisection / aggregation tree +// ============================================================================ + +/// The bisection tree produced by recursive min-cut partitioning: leaves are +/// shard ids, internal nodes are the two halves of a min-cut split. Reused as +/// the proof-aggregation tree so cross-shard assumptions discharge at the lowest +/// common ancestor of the shards that share them (see module docs and +/// [`agg_plan`]). Serialized into the `.ixes` manifest so the prover can fold +/// along it. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum AggNode { + /// A single shard, identified by its id. + Leaf(u32), + /// A min-cut split into a left and right subtree. + Internal(Box, Box), +} + +impl AggNode { + /// Number of shard leaves under this node. + pub fn num_leaves(&self) -> usize { + match self { + AggNode::Leaf(_) => 1, + AggNode::Internal(l, r) => l.num_leaves() + r.num_leaves(), + } + } + + /// The smallest shard id under this node — its left-to-right position. Used to + /// keep aggregation children in shard order (so the subject merkle-fold stays + /// the left-associative order the agg guest reproduces). + pub fn min_leaf(&self) -> u32 { + match self { + AggNode::Leaf(id) => *id, + AggNode::Internal(l, _) => l.min_leaf(), + } + } + + /// Prune to the leaves selected by `keep`: dropped leaves disappear and an + /// internal node that loses one child collapses to the survivor. Returns + /// `None` when nothing remains. Lets a prover fold along the tree even when + /// only a subset of shards was proven (empty shards, `--only-shard`). + pub fn prune(&self, keep: &impl Fn(u32) -> bool) -> Option { + match self { + AggNode::Leaf(id) => keep(*id).then_some(AggNode::Leaf(*id)), + AggNode::Internal(l, r) => match (l.prune(keep), r.prune(keep)) { + (Some(a), Some(b)) => Some(AggNode::Internal(Box::new(a), Box::new(b))), + (Some(a), None) | (None, Some(a)) => Some(a), + (None, None) => None, + }, + } + } + + /// Preorder serialization: `0u8, id(4)` for a leaf; `1u8, , ` for + /// an internal node. + fn put(&self, out: &mut Vec) { + match self { + AggNode::Leaf(id) => { + out.push(0); + out.extend_from_slice(&id.to_le_bytes()); + }, + AggNode::Internal(l, r) => { + out.push(1); + l.put(out); + r.put(out); + }, + } + } + + /// Inverse of [`Self::put`], reading from a cursor. + fn get(c: &mut Cur<'_>) -> Result { + match c.u8()? { + 0 => Ok(AggNode::Leaf(c.u32()?)), + 1 => { + let l = AggNode::get(c)?; + let r = AggNode::get(c)?; + Ok(AggNode::Internal(Box::new(l), Box::new(r))) + }, + t => Err(format!("bad AggNode tag {t}")), + } + } +} + +/// One step of an aggregation fold plan, in post order: every `Agg`'s child +/// indices refer to *earlier* entries in the plan, and the last entry is the +/// root. The prover materializes a slot per entry — `Leaf(id)` is shard `id`'s +/// leaf proof; `Agg(children)` folds those slots' proofs in one agg call. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum FoldOp { + Leaf(u32), + Agg(Vec), +} + +/// Lower the bisection tree to an arity-bounded fold plan. The binary tree is +/// *collapsed* so each agg call folds up to `arity` whole subtrees (never +/// splitting a subtree across calls), keeping the agg-proof count ~`N/(arity-1)` +/// — a strict binary fold would be ~`N` agg proofs, dominating cost. Children of +/// each agg are emitted in shard order so the subject merkle-fold matches the +/// guest's left-associative join. +pub fn agg_plan(tree: &AggNode, arity: usize) -> Vec { + let arity = arity.max(2); + let mut ops: Vec = Vec::new(); + build_plan(tree, arity, &mut ops); + ops +} + +/// Emit `node`'s plan entries; return the index of the entry representing it. +fn build_plan(node: &AggNode, arity: usize, ops: &mut Vec) -> usize { + match node { + AggNode::Leaf(id) => { + ops.push(FoldOp::Leaf(*id)); + ops.len() - 1 + }, + AggNode::Internal(l, r) => { + // Collapse the binary subtree into up to `arity` child subtrees: start + // from the two halves and repeatedly split the *largest* still-internal + // frontier node until we have `arity` children or all are leaves. This + // keeps tightly-coupled siblings together while bounding the fan-in. + let mut frontier: Vec<&AggNode> = vec![l, r]; + while frontier.len() < arity { + let largest = frontier + .iter() + .enumerate() + .filter(|(_, nd)| matches!(nd, AggNode::Internal(_, _))) + .max_by_key(|(_, nd)| nd.num_leaves()) + .map(|(i, _)| i); + let Some(i) = largest else { break }; // all leaves: cannot expand + let AggNode::Internal(l, r) = frontier.swap_remove(i) else { + unreachable!() + }; + frontier.push(l); + frontier.push(r); + } + // Shard order keeps the merkle subject-fold left-associative. + frontier.sort_by_key(|nd| nd.min_leaf()); + let child_idxs: Vec = + frontier.iter().map(|c| build_plan(c, arity, ops)).collect(); + ops.push(FoldOp::Agg(child_idxs)); + ops.len() - 1 + }, } } @@ -340,14 +532,14 @@ fn rec_bisect( epsilon: f64, shard_of: &[AtomicU32], prog: &PartitionProgress, -) { +) -> AggNode { if k <= 1 || sub.num_vertices() <= 1 { // A leaf (or a subtree with ≤1 vertex): everything here is one shard. for &g in &sub.global { shard_of[g as usize].store(base, Ordering::Relaxed); } prog.leaf(); - return; + return AggNode::Leaf(base); } prog.bisecting(sub.num_vertices(), k); let side = bisect(sub, epsilon); @@ -365,7 +557,7 @@ fn rec_bisect( shard_of[g as usize].store(base, Ordering::Relaxed); } prog.leaf(); - return; + return AggNode::Leaf(base); } // Allocate leaves ~proportional to heartbeat mass, clamped to [1, side size]. // `lo`/`hi` are feasible (lo ≤ hi) because nl + nr ≥ k with nl, nr ≥ 1. @@ -379,16 +571,21 @@ fn rec_bisect( let rbase = base + k_left as u32; // The two halves are independent (disjoint blocks) — recurse them in parallel // when the work is large enough to amortize the join. The result is identical - // to serial execution (each subtree is deterministic). - if nl + nr >= PARALLEL_THRESHOLD { + // to serial execution (each subtree is deterministic). Each call returns its + // subtree's `AggNode`; the two combine into this node's binary split — the + // bisection tree mirrors the recursion exactly. + let (ln, rn) = if nl + nr >= PARALLEL_THRESHOLD { rayon::join( || rec_bisect(&left, k_left, base, epsilon, shard_of, prog), || rec_bisect(&right, k - k_left, rbase, epsilon, shard_of, prog), - ); + ) } else { - rec_bisect(&left, k_left, base, epsilon, shard_of, prog); - rec_bisect(&right, k - k_left, rbase, epsilon, shard_of, prog); - } + ( + rec_bisect(&left, k_left, base, epsilon, shard_of, prog), + rec_bisect(&right, k - k_left, rbase, epsilon, shard_of, prog), + ) + }; + AggNode::Internal(Box::new(ln), Box::new(rn)) } /// Progress reporter for [`Hypergraph::partition`]. The partitioner is silent @@ -1082,6 +1279,10 @@ pub struct ShardManifest { pub shards: Vec, /// Total cross-shard ingress bytes (the km1 objective). pub total_cross_ingress: u128, + /// The bisection tree over the shard ids, for proof aggregation. `None` on + /// manifests written before this section existed (the prover then falls back + /// to a flat tree-fold). Set by [`Self::with_tree`]. + pub tree: Option, } impl ShardManifest { @@ -1139,9 +1340,18 @@ impl ShardManifest { num_shards: num_shards as u32, shards, total_cross_ingress: total_cross, + tree: None, } } + /// Attach the bisection tree (from [`Hypergraph::partition_with_tree`]) for + /// tree-aligned aggregation. + #[must_use] + pub fn with_tree(mut self, tree: AggNode) -> Self { + self.tree = Some(tree); + self + } + /// A human-readable what-if summary line. pub fn summary(&self) -> String { let hbs: Vec = self.shards.iter().map(|s| s.heartbeats).collect(); @@ -1203,6 +1413,16 @@ impl ShardManifest { put_addrs(&mut out, &sh.blocks); put_addrs(&mut out, &sh.foreign_blocks); } + // Trailing optional bisection-tree section: presence byte then preorder + // tree. Appended after the shards so older readers that stop at the shard + // count simply ignore it, and `from_bytes` treats end-of-input as `None`. + match &self.tree { + Some(t) => { + out.push(1); + t.put(&mut out); + }, + None => out.push(0), + } out } @@ -1233,10 +1453,18 @@ impl ShardManifest { assumption_root, }); } + // Optional trailing tree section. Absent (end-of-input) on pre-tree + // manifests, or an explicit `0` presence byte. + let tree = if c.pos < c.buf.len() { + if c.u8()? == 1 { Some(AggNode::get(&mut c)?) } else { None } + } else { + None + }; Ok(ShardManifest { num_shards: num_shards as u32, shards, total_cross_ingress, + tree, }) } } @@ -1285,7 +1513,7 @@ impl<'a> Cur<'a> { } } -/// Read a `.ixesp`, partition into `num_shards` shards, and emit a manifest with +/// Read a `.ixprof`, partition into `num_shards` shards, and emit a manifest with /// per-shard cost metrics, foreign-block sets, and (delta-based) assumption /// roots. Optionally writes the manifest (`.ixes`). Returns a what-if report. /// @@ -1297,6 +1525,7 @@ pub fn shard_esp( esp_path: &str, num_shards: usize, balance: f64, + parallelism: usize, out_path: Option<&str>, ) -> Result { let bytes = @@ -1304,11 +1533,12 @@ pub fn shard_esp( let profile = BlockProfile::from_bytes(&bytes) .map_err(|e| format!("parse {esp_path}: {e}"))?; let h = Hypergraph::from_profile(&profile); - let shard_of = h.partition(num_shards, balance); - let mut manifest = ShardManifest::build(&profile, &shard_of, num_shards); + let (shard_of, tree) = h.partition_with_tree(num_shards, balance); + let mut manifest = + ShardManifest::build(&profile, &shard_of, num_shards).with_tree(tree); for shard in &mut manifest.shards { shard.assumption_root = - crate::ix::ixon::merkle::merkle_root_canonical(&shard.foreign_blocks); + ixon::merkle::merkle_root_canonical(&shard.foreign_blocks); } if let Some(op) = out_path { std::fs::write(op, manifest.to_bytes()) @@ -1331,20 +1561,463 @@ pub fn shard_esp( "" }; Ok(format!( - "blocks={} delta_edges={} nets={}\n{}\nlargest_block_hb={}{}", + "blocks={} delta_edges={} nets={}\n{}\n{}\nlargest_block_hb={}{}", profile.num_blocks(), profile.num_edges(), h.num_nets(), manifest.summary(), + prove_report(&profile, &shard_of, &manifest, parallelism), max_block_hb, note, )) } +/// Like [`shard_esp`] but sized to a per-shard Zisk **cycle** budget +/// (`max_cycles`) rather than a fixed shard count: grows `N` until the heaviest +/// splittable shard fits the budget (see [`partition_for_cycle_cap`]). Use +/// [`cycle_cap_for_ram`] to derive `max_cycles` from a host-RAM target. +pub fn shard_esp_cap( + esp_path: &str, + max_cycles: u64, + balance: f64, + parallelism: usize, + out_path: Option<&str>, +) -> Result { + let bytes = + std::fs::read(esp_path).map_err(|e| format!("read {esp_path}: {e}"))?; + let profile = BlockProfile::from_bytes(&bytes) + .map_err(|e| format!("parse {esp_path}: {e}"))?; + let plan = partition_for_cycle_cap(&profile, max_cycles, balance); + let mut manifest = + ShardManifest::build(&profile, &plan.shard_of, plan.num_shards) + .with_tree(plan.tree); + for shard in &mut manifest.shards { + shard.assumption_root = + ixon::merkle::merkle_root_canonical(&shard.foreign_blocks); + } + if let Some(op) = out_path { + std::fs::write(op, manifest.to_bytes()) + .map_err(|e| format!("write {op}: {e}"))?; + } + let note = if plan.infeasible_atomic_floor { + "\n [INFEASIBLE: a single atomic block exceeds the cap — split it upstream, raise the cap, or use a bigger box]" + } else { + "" + }; + Ok(format!( + "blocks={} delta_edges={} max_cycles={} → {} shards (packed to cap)\n{}\n{}\nmax_shard_steps={} (cap {}, incl. floor {} + cross-ingress) largest_block_steps={}{}", + profile.num_blocks(), + profile.num_edges(), + max_cycles, + plan.num_shards, + manifest.summary(), + prove_report(&profile, &plan.shard_of, &manifest, parallelism), + plan.max_shard_steps, + max_cycles, + SHARD_STEP_FLOOR, + plan.largest_block_steps, + note, + )) +} + +/// Calibrated Zisk guest-STEP cost model — the **single source of truth** for +/// predicting a block's in-circuit step contribution: reduction work +/// (`heartbeats` + substitution-node visits `subst`) plus a small ingress term +/// (`serialized_size` bytes). These are **best-fit** coefficients (over 76 +/// Init/Std/Mathlib constants + 12 shards; the cycle model is `180M floor + +/// 162k·hb + 4.3k·subst + 0.65k·bytes`, MAPE 9%). One definition, used +/// everywhere: the `ix profile` breakdown, per-shard prediction, and the packer's +/// cap test. No conservatism is baked into the coefficients — that would distort +/// packing; the cap's safety margin is [`RAM_USABLE_FRAC`] in +/// [`cycle_cap_for_ram`], reinforced by the model's own ~1.1× over-prediction. +pub const STEPS_PER_HEARTBEAT: u64 = 162_339; +pub const STEPS_PER_SUBST: u64 = 4_276; +pub const STEPS_PER_INGRESS_BYTE: u64 = 652; +/// Fixed per-shard (per-leaf) guest-STEP floor (the model intercept): proof/base +/// setup plus the foreign-dependency ingress a shard re-pays. +pub const SHARD_STEP_FLOOR: u64 = 180_000_000; + +/// Predicted Zisk guest STEPS contributed by a single block (reduction + its own +/// ingress). The per-shard floor and any cross-shard re-ingress are added at the +/// shard level, not here. +pub fn block_step_cost(b: &BlockEntry) -> u64 { + STEPS_PER_HEARTBEAT + .saturating_mul(b.heartbeats) + .saturating_add(STEPS_PER_SUBST.saturating_mul(b.subst)) + .saturating_add( + STEPS_PER_INGRESS_BYTE.saturating_mul(u64::from(b.serialized_size)), + ) +} + +/// The per-shard cycle cap implied by a machine's total RAM — so callers can +/// size shards straight from `MemTotal` without ever picking a budget. Inverts +/// the measured single-leaf prover model on this setup +/// (`peak_RAM_GiB ≈ 50 + 33 × steps_billions`, measured by a guarded 7-shard GPU +/// prove sweep over 0.27–3.79e9-step Init shards, R²=0.99, `--max-witness-stored +/// 5`) at [`RAM_USABLE_FRAC`] of RAM (reserving the rest for the OS, cross-shard +/// re-ingress, and run-to-run variance). Returns 0 when the box can't even hold the ~50 GiB +/// prover base (nothing will prove). Approximate by design — pair with +/// [`partition_for_cycle_cap`] to get N. The earlier `45 + 32` model was +/// optimistic on both base and slope (a 4.84e9-step shard it sized for a 200 GB +/// target actually used ~225 GB). +/// Measured prover-RAM model (the single source of truth, used by both +/// [`cycle_cap_for_ram`] and [`ram_gib_for_steps`]): peak host RAM ≈ +/// `RAM_BASE_GIB + RAM_GIB_PER_BCYCLE × steps_billions` (at +/// `--max-witness-stored 5`). +pub const RAM_BASE_GIB: f64 = 50.0; +pub const RAM_GIB_PER_BCYCLE: f64 = 33.0; +/// Usable fraction of a host-RAM budget (headroom for OS + variance) — applied +/// to whatever budget the caller gives (explicit `--max-ram`, or detected +/// system RAM by default). +pub const RAM_USABLE_FRAC: f64 = 0.85; + +/// Predicted peak prover RAM (GiB) for a leaf of `steps` guest STEPS. +pub fn ram_gib_for_steps(steps: u64) -> f64 { + RAM_BASE_GIB + RAM_GIB_PER_BCYCLE * (steps as f64 / 1e9) +} + +pub fn cycle_cap_for_ram(ram_gb: f64) -> u64 { + let headroom = ram_gb * RAM_USABLE_FRAC - RAM_BASE_GIB; + if headroom <= 0.0 { + return 0; + } + (headroom / RAM_GIB_PER_BCYCLE * 1e9) as u64 +} + +/// Measured single-GPU **leaf prove time**: `≈ PROVE_SETUP_SECS + +/// PROVE_SECS_PER_BCYCLE × steps_billions` per shard (RTX PRO 6000, +/// `--max-witness-stored 5`). Aggregation adds a smaller per-fold term this model +/// omits — minutes next to hours of leaf proving at large shard counts. +pub const PROVE_SETUP_SECS: f64 = 54.0; +pub const PROVE_SECS_PER_BCYCLE: f64 = 158.0; + +/// Predicted single-shard leaf prove time (seconds) for a leaf of `steps`. +pub fn shard_prove_secs(steps: u64) -> f64 { + PROVE_SETUP_SECS + PROVE_SECS_PER_BCYCLE * (steps as f64 / 1e9) +} + +/// Whole-workload prove-time estimate over a partition's per-shard step counts. +pub struct ProveEstimate { + /// Σ predicted guest STEPS over all shards (incl. per-shard floor + ingress). + pub total_steps: u64, + /// Sum of per-shard leaf prove times — wall clock with a single prover. + pub seq_secs: f64, + /// Wall clock with `parallelism` provers; `seq_secs` at parallelism 1. + pub wall_secs: f64, + /// Provers assumed (≥1). + pub parallelism: usize, +} + +/// Estimate whole-workload prove time from per-shard predicted STEPS. With +/// `parallelism` provers the wall clock is bounded below by both an even split +/// and the single slowest shard (a makespan can beat neither); for the +/// near-uniform shards the packer produces the even split dominates. +pub fn prove_estimate( + per_shard_steps: &[u64], + parallelism: usize, +) -> ProveEstimate { + let p = parallelism.max(1); + let total_steps = + per_shard_steps.iter().fold(0u64, |a, &s| a.saturating_add(s)); + let seq_secs: f64 = + per_shard_steps.iter().map(|&s| shard_prove_secs(s)).sum(); + let max_secs = + per_shard_steps.iter().map(|&s| shard_prove_secs(s)).fold(0.0, f64::max); + let wall_secs = (seq_secs / p as f64).max(max_secs); + ProveEstimate { total_steps, seq_secs, wall_secs, parallelism: p } +} + +/// Per-shard predicted full STEPS (floor + member step-cost + cross-ingress), +/// indexed by shard id, from a built manifest and its block assignment. +fn per_shard_steps( + profile: &BlockProfile, + shard_of: &[u32], + manifest: &ShardManifest, +) -> Vec { + let mut steps = vec![0u64; manifest.shards.len()]; + for (b, &s) in shard_of.iter().enumerate() { + steps[s as usize] = steps[s as usize] + .saturating_add(block_step_cost(profile.block(b as u32))); + } + for sh in &manifest.shards { + let i = sh.id as usize; + steps[i] = steps[i] + .saturating_add(SHARD_STEP_FLOOR) + .saturating_add(STEPS_PER_INGRESS_BYTE.saturating_mul(sh.cross_ingress)); + } + steps +} + +/// Human-readable duration for the prove-time report. +fn fmt_duration(secs: f64) -> String { + if secs >= 3600.0 { + format!("{:.1}h", secs / 3600.0) + } else if secs >= 60.0 { + format!("{:.0}m", secs / 60.0) + } else { + format!("{secs:.0}s") + } +} + +/// One-line prove-time report fragment shared by [`shard_esp`] and +/// [`shard_esp_cap`]: total steps across all shards plus the wall-clock estimate +/// at the requested `parallelism` (and the sequential baseline). +fn prove_report( + profile: &BlockProfile, + shard_of: &[u32], + manifest: &ShardManifest, + parallelism: usize, +) -> String { + let est = + prove_estimate(&per_shard_steps(profile, shard_of, manifest), parallelism); + format!( + "total_steps={} → prove est ~{} wall at parallelism={} (sequential ~{}); \ + leaf model {PROVE_SETUP_SECS:.0}s + {PROVE_SECS_PER_BCYCLE:.0}s·Bsteps/shard, aggregation extra", + est.total_steps, + fmt_duration(est.wall_secs), + est.parallelism, + fmt_duration(est.seq_secs), + ) +} + +/// A partition sized to a per-shard Zisk **cycle** budget (rather than a fixed +/// shard count). See [`partition_for_cycle_cap`]. +pub struct BudgetPlan { + /// Chosen shard count. + pub num_shards: usize, + /// Shard assignment per block id (as [`Hypergraph::partition`] returns). + pub shard_of: Vec, + /// The bisection tree for the chosen partition, for tree-aligned aggregation. + pub tree: AggNode, + /// Per-shard predicted-STEPS cap: `max_cycles − SHARD_STEP_FLOOR` (the block + /// step-costs that may sum into one shard before the floor pushes it over + /// `max_cycles`). + pub step_cap: u64, + /// Heaviest shard's **full** predicted guest STEPS — `SHARD_STEP_FLOOR + + /// Σ block_step_cost + STEPS_PER_INGRESS_BYTE × cross_ingress_bytes`. The + /// packer keeps this `≤ max_cycles` (cross-ingress included), so it is the + /// tightest single number to compare against the cap / RAM budget. + pub max_shard_steps: u64, + /// Largest single (atomic) block's [`block_step_cost`] — the hard floor on + /// `max_shard_steps` that no shard count can beat. + pub largest_block_steps: u64, + /// True when the largest atomic block alone exceeds the cap: the budget is + /// infeasible by sharding (split that mutual block upstream, or raise the cap + /// / use a bigger box). + pub infeasible_atomic_floor: bool, +} + +/// Size a partition to a per-shard **cycle** (guest-STEP) budget by **bin-packing +/// to the cap**, not by balancing into a fixed shard count. +/// +/// `max_cycles` is the ceiling on a single shard's in-circuit Zisk guest steps +/// — the leaf prover's trace size, which is what sets peak prover RAM. Get it +/// from a host-RAM budget with [`cycle_cap_for_ram`] (the measured prover model +/// `peak_RAM_GiB ≈ 50 + 33 × steps_billions`). +/// +/// **Why packing, not balancing.** A shard's predicted STEPS is +/// `SHARD_STEP_FLOOR + Σ block_step_cost + STEPS_PER_INGRESS_BYTE × +/// cross_ingress_bytes` (member reduction + own ingress, plus the foreign +/// dependencies it re-ingresses). The goal is the *fewest* shards that each stay +/// under the cap — not uniform shards. Balancing into `N = ⌈total/cap⌉` and +/// growing `N` whenever imbalance pushed the heaviest shard over the cap (the +/// old approach) *over-sharded*: it spread work evenly, so every shard sat +/// partially full and a single un-equalizable block forced extra shards. Packing +/// instead fills each shard as close to the cap as the dependency structure +/// allows and only opens a new shard when the next block won't fit — giving +/// `⌈total/cap⌉` shards plus a small packing remainder, each maximally +/// performant. Lumpy (a near-full shard beside a partial one) is fine. +/// +/// **How dependency overlap is packed.** We first cut a *fine* min-cut partition +/// ([`PACK_PIECES_PER_CAP`] pieces per cap) purely to obtain a **cut-coherent +/// order**: a DFS of the bisection tree visits each tightly-coupled subtree +/// before moving on, so dependency-overlapping blocks are contiguous. Greedy +/// next-fit along that order therefore packs overlapping blocks into the same +/// shard — keeping their shared dependencies in-shard so they are not re-paid as +/// cross-ingress — and only the thin seams between coherent runs cross a shard +/// boundary. The cap test includes the live cross-ingress, so a shard packed +/// "to the cap" still executes under it. +/// +/// Flags `infeasible_atomic_floor` when one block alone (its floor + reduction + +/// own + dependency ingress) exceeds the cap — it cannot be split, so the cap is +/// unreachable by sharding (split that mutual block upstream, or use a bigger +/// box). The block is still emitted in its own shard, best-effort. +pub fn partition_for_cycle_cap( + profile: &BlockProfile, + max_cycles: u64, + epsilon: f64, +) -> BudgetPlan { + // Safety margin lives in `cycle_cap_for_ram` (`RAM_USABLE_FRAC` withholds 15% + // of the RAM budget) plus the model's own ~1.1× over-prediction — so the cap is + // applied directly, no extra factor. + let step_cap = max_cycles.saturating_sub(SHARD_STEP_FLOOR).max(1); + let nblocks = profile.num_blocks(); + let largest_block = + profile.blocks().iter().map(block_step_cost).max().unwrap_or(0); + let size = |b: u32| u64::from(profile.block(b).serialized_size); + + if nblocks == 0 { + return BudgetPlan { + num_shards: 1, + shard_of: Vec::new(), + tree: AggNode::Leaf(0), + step_cap, + max_shard_steps: SHARD_STEP_FLOOR, + largest_block_steps: 0, + infeasible_atomic_floor: false, + }; + } + + // 1. Cut-coherent block order. A fine min-cut pre-partition's bisection tree, + // read in DFS order, lays tightly-coupled blocks contiguously so the packer + // keeps dependency overlap within a shard. Sized to ~PACK_PIECES_PER_CAP + // pieces per cap (bounded by the block count); when everything fits one cap + // this collapses to a trivial order. + let total: u128 = + profile.blocks().iter().map(|b| u128::from(block_step_cost(b))).sum(); + let pieces = ((total.saturating_mul(PACK_PIECES_PER_CAP)) + / u128::from(step_cap)) as usize; + let n_fine = pieces.clamp(1, nblocks); + let order: Vec = if n_fine < 2 { + (0..nblocks as u32).collect() + } else { + let h = Hypergraph::from_profile(profile); + let (fine_of, fine_tree) = h.partition_with_tree(n_fine, epsilon); + let mut leaf_order = Vec::with_capacity(n_fine); + dfs_leaf_order(&fine_tree, &mut leaf_order); + let mut rank = vec![0u32; n_fine]; + for (r, &sid) in leaf_order.iter().enumerate() { + rank[sid as usize] = r as u32; + } + let mut order: Vec = (0..nblocks as u32).collect(); + order.sort_by_key(|&b| (rank[fine_of[b as usize] as usize], b)); + order + }; + + // 2. Greedy next-fit packing to the cap, with live cross-ingress accounting. + // A shard accumulates members until adding the next block would push its + // full predicted STEPS over `max_cycles`, then a new shard opens. Foreign + // dependency bytes (`foreign`) shrink when a later member is itself one of + // the current shard's pending dependencies — exactly the overlap packing + // saves. + let mut shard_of = vec![0u32; nblocks]; + let mut members: FxHashSet = FxHashSet::default(); + let mut foreign: FxHashSet = FxHashSet::default(); + let mut seen: FxHashSet = FxHashSet::default(); + let mut member_cost = 0u64; + let mut foreign_bytes = 0u64; + let mut cur = 0u32; + let mut max_shard_steps = 0u64; + let mut infeasible = false; + let predicted = |member_cost: u64, foreign_bytes: u64| -> u64 { + SHARD_STEP_FLOOR + .saturating_add(member_cost) + .saturating_add(STEPS_PER_INGRESS_BYTE.saturating_mul(foreign_bytes)) + }; + + for (i, &b) in order.iter().enumerate() { + let bc = block_step_cost(profile.block(b)); + // Foreign-byte delta if `b` joins the current shard: `b` itself drops out of + // foreign if a prior member depended on it; `b`'s own producers join unless + // already member/foreign. + seen.clear(); + let mut added = 0u64; + for &p in profile.producers(b) { + if p != b + && !members.contains(&p) + && !foreign.contains(&p) + && seen.insert(p) + { + added = added.saturating_add(size(p)); + } + } + let removed = if foreign.contains(&b) { size(b) } else { 0 }; + let tent_fbytes = + foreign_bytes.saturating_add(added).saturating_sub(removed); + let tentative = predicted(member_cost.saturating_add(bc), tent_fbytes); + + // Open a new shard when the current one is non-empty and `b` won't fit. The + // first block of a run always goes in (a lone over-cap block is flagged + // infeasible below, not split — it can't be). + if !members.is_empty() && tentative > max_cycles { + max_shard_steps = + max_shard_steps.max(predicted(member_cost, foreign_bytes)); + cur += 1; + members.clear(); + foreign.clear(); + member_cost = 0; + foreign_bytes = 0; + } + + // Commit `b` into the current shard. + if foreign.remove(&b) { + foreign_bytes = foreign_bytes.saturating_sub(size(b)); + } + members.insert(b); + member_cost = member_cost.saturating_add(bc); + for &p in profile.producers(b) { + if p != b && !members.contains(&p) && foreign.insert(p) { + foreign_bytes = foreign_bytes.saturating_add(size(p)); + } + } + shard_of[b as usize] = cur; + if members.len() == 1 && predicted(member_cost, foreign_bytes) > max_cycles + { + infeasible = true; // this block alone overflows the cap + } + if i == order.len() - 1 { + max_shard_steps = + max_shard_steps.max(predicted(member_cost, foreign_bytes)); + } + } + + let num_shards = (cur + 1) as usize; + // 3. Balanced aggregation tree over the shard sequence. Shards are numbered in + // packing (cut-coherent) order, so adjacent ids are coupled — a balanced + // binary tree over `0..num_shards` keeps coupled shards as siblings, so + // cross-shard assumptions discharge low in the fold. + let tree = balanced_agg_tree(0, num_shards as u32); + + BudgetPlan { + num_shards, + shard_of, + tree, + step_cap, + max_shard_steps, + largest_block_steps: largest_block, + infeasible_atomic_floor: infeasible, + } +} + +/// Collect the leaf shard ids of an [`AggNode`] in left-to-right DFS order. +fn dfs_leaf_order(node: &AggNode, out: &mut Vec) { + match node { + AggNode::Leaf(id) => out.push(*id), + AggNode::Internal(l, r) => { + dfs_leaf_order(l, out); + dfs_leaf_order(r, out); + }, + } +} + +/// A balanced binary [`AggNode`] over the contiguous shard-id range `lo..hi`. +fn balanced_agg_tree(lo: u32, hi: u32) -> AggNode { + debug_assert!(hi > lo); + if hi - lo <= 1 { + AggNode::Leaf(lo) + } else { + let mid = lo + (hi - lo) / 2; + AggNode::Internal( + Box::new(balanced_agg_tree(lo, mid)), + Box::new(balanced_agg_tree(mid, hi)), + ) + } +} + #[cfg(test)] mod tests { use super::*; - use crate::ix::profile::ProfileBuilder; + use crate::profile::ProfileBuilder; fn addr(byte: u8) -> Address { Address::from_slice(&[byte; 32]).unwrap() @@ -1365,7 +2038,7 @@ mod tests { fn two_clusters() -> BlockProfile { let mut b = ProfileBuilder::new(); for i in 1..=6u8 { - b.block(addr(i), 100, 1000, 1); + b.block(addr(i), 100, 1000, 1, 0); } // intra cluster A b.delta_edge(addr(1), addr(2)); @@ -1424,7 +2097,7 @@ mod tests { for c in 0..4u8 { let base = c * 4 + 1; for k in 0..4u8 { - b.block(addr(base + k), 100, 500, 1); + b.block(addr(base + k), 100, 500, 1, 0); } b.delta_edge(addr(base), addr(base + 1)); b.delta_edge(addr(base + 1), addr(base + 2)); @@ -1466,9 +2139,9 @@ mod tests { // non-empty (parallelism is the goal), even though heartbeat balance is // impossible. let mut b = ProfileBuilder::new(); - b.block(addr(1), 30_000, 100, 1); // ~30x a light block + b.block(addr(1), 30_000, 100, 1, 0); // ~30x a light block for i in 2..=65u8 { - b.block(addr(i), 1000, 100, 1); + b.block(addr(i), 1000, 100, 1, 0); } for i in 2..=64u8 { b.delta_edge(addr(i), addr(i + 1)); @@ -1485,20 +2158,109 @@ mod tests { ); } + /// Full predicted STEPS of a shard under a partition: floor + member step-cost + /// + cross-ingress (foreign producers' bytes). Mirrors the packer's cap test. + fn shard_predicted(p: &BlockProfile, shard_of: &[u32], s: u32) -> u64 { + let members: FxHashSet = (0..p.num_blocks() as u32) + .filter(|&b| shard_of[b as usize] == s) + .collect(); + let member_cost: u64 = + members.iter().map(|&b| block_step_cost(p.block(b))).sum(); + let mut foreign: FxHashSet = FxHashSet::default(); + for &b in &members { + for &pr in p.producers(b) { + if !members.contains(&pr) { + foreign.insert(pr); + } + } + } + let foreign_bytes: u64 = + foreign.iter().map(|&pr| u64::from(p.block(pr).serialized_size)).sum(); + SHARD_STEP_FLOOR + member_cost + STEPS_PER_INGRESS_BYTE * foreign_bytes + } + + #[test] + fn cap_packs_to_minimal_shards_under_cap() { + // 40 blocks, each ≈162.339M steps (hb=1000), no ingress. With a 1e9 cap the + // floor is 180M, leaving room for 5 blocks/shard (5×162.339M = 811.7M ≤ + // 820M; a 6th overflows). Packing should produce exactly ⌈40/5⌉ = 8 shards, + // each under the cap — no over-sharding from balancing. + let mut b = ProfileBuilder::new(); + for i in 1..=40u8 { + b.block(addr(i), 1000, 0, 0, 0); + } + for i in 1..40u8 { + b.delta_edge(addr(i), addr(i + 1)); + } + let p = b.finish(); + let max_cycles = 1_000_000_000u64; + let plan = partition_for_cycle_cap(&p, max_cycles, 0.05); + assert_eq!(plan.num_shards, 8, "should pack to the minimal 8 shards"); + assert!(!plan.infeasible_atomic_floor); + for s in 0..plan.num_shards as u32 { + let pred = shard_predicted(&p, &plan.shard_of, s); + assert!( + pred <= max_cycles, + "shard {s} predicted {pred} exceeds cap {max_cycles}" + ); + } + assert!(plan.max_shard_steps <= max_cycles); + // Every block assigned to a real shard; tree leaves match the shard ids. + assert_eq!(plan.tree.num_leaves(), plan.num_shards); + } + + #[test] + fn cap_cross_ingress_counts_against_the_cap() { + // Two blocks that each delta-unfold a heavy shared dependency `dep` (large + // serialized_size). If they land in different shards, BOTH re-ingress `dep`. + // The packer must keep them together (coherent order) so `dep` is paid once, + // and must count `dep`'s bytes when deciding the cap. + let mut b = ProfileBuilder::new(); + b.block(addr(1), 10, 4_000_000, 0, 0); // dep: ~2.6e9 ingress steps if foreign + b.block(addr(2), 100, 0, 0, 0); + b.block(addr(3), 100, 0, 0, 0); + b.delta_edge(addr(2), addr(1)); // 2 unfolds dep + b.delta_edge(addr(3), addr(1)); // 3 unfolds dep + let p = b.finish(); + // Cap large enough to hold everything in one shard. + let plan = partition_for_cycle_cap(&p, 10_000_000_000, 0.05); + for s in 0..plan.num_shards as u32 { + let pred = shard_predicted(&p, &plan.shard_of, s); + assert!(pred <= 10_000_000_000, "shard {s} over cap: {pred}"); + } + } + + #[test] + fn cap_flags_infeasible_atomic_block() { + // A single block whose own predicted STEPS exceed the cap cannot be split — + // it is emitted alone and the plan is flagged infeasible. + let mut b = ProfileBuilder::new(); + b.block(addr(1), 100_000, 0, 0, 0); // ~16.2e9 steps, far over a 1e9 cap + b.block(addr(2), 100, 0, 0, 0); + let p = b.finish(); + let plan = partition_for_cycle_cap(&p, 1_000_000_000, 0.05); + assert!(plan.infeasible_atomic_floor, "oversized atomic block must flag"); + } + #[test] fn shard_esp_file_roundtrip() { - // Exercise the CLI's shard path: write a .ixesp, run shard_esp, read back + // Exercise the CLI's shard path: write a .ixprof, run shard_esp, read back // the .ixes manifest. let p = two_clusters(); let dir = std::env::temp_dir(); let pid = std::process::id(); - let prof = dir.join(format!("ix_shard_rt_{pid}.ixesp")); + let prof = dir.join(format!("ix_shard_rt_{pid}.ixprof")); let shard = dir.join(format!("ix_shard_rt_{pid}.ixes")); std::fs::write(&prof, p.to_bytes()).unwrap(); - let report = - shard_esp(prof.to_str().unwrap(), 2, 0.10, Some(shard.to_str().unwrap())) - .unwrap(); + let report = shard_esp( + prof.to_str().unwrap(), + 2, + 0.10, + 1, + Some(shard.to_str().unwrap()), + ) + .unwrap(); assert!(report.contains("shards=2"), "report was: {report}"); let m = ShardManifest::from_bytes(&std::fs::read(&shard).unwrap()).unwrap(); @@ -1560,7 +2322,7 @@ mod tests { fn two_big_clusters(m: u32) -> BlockProfile { let mut b = ProfileBuilder::new(); for i in 0..2 * m { - b.block(addr_u32(i + 1), 100, 1000, 1); + b.block(addr_u32(i + 1), 100, 1000, 1, 0); } for i in 0..m { // cluster A cycle over addrs 1..=m @@ -1622,9 +2384,9 @@ mod tests { // candidates, or a shard ends up empty. (Regression guard.) let mut b = ProfileBuilder::new(); let m = 800u32; - b.block(addr_u32(1), 5_000_000, 100, 1); // the giant + b.block(addr_u32(1), 5_000_000, 100, 1, 0); // the giant for i in 2..=m { - b.block(addr_u32(i), 1000, 100, 1); + b.block(addr_u32(i), 1000, 100, 1, 0); } for i in 1..m { b.delta_edge(addr_u32(i), addr_u32(i + 1)); // a chain (incl. the giant) @@ -1641,4 +2403,129 @@ mod tests { ); } } + + // ---- Bisection / aggregation tree ---- + + fn leaf(id: u32) -> AggNode { + AggNode::Leaf(id) + } + fn node(l: AggNode, r: AggNode) -> AggNode { + AggNode::Internal(Box::new(l), Box::new(r)) + } + + #[test] + fn partition_with_tree_leaves_match_shards() { + // 360 blocks → 4 shards. The tree's leaves must be exactly shard ids 0..4. + let p = two_big_clusters(180); + let h = Hypergraph::from_profile(&p); + let (shard_of, tree) = h.partition_with_tree(4, 0.10); + assert_eq!(tree.num_leaves(), 4); + let mut ids = Vec::new(); + fn collect(n: &AggNode, out: &mut Vec) { + match n { + AggNode::Leaf(id) => out.push(*id), + AggNode::Internal(l, r) => { + collect(l, out); + collect(r, out); + }, + } + } + collect(&tree, &mut ids); + ids.sort_unstable(); + assert_eq!(ids, vec![0, 1, 2, 3]); + // Every shard id assigned to some block must appear as a leaf. + let used: std::collections::BTreeSet = + shard_of.iter().copied().collect(); + assert!(used.iter().all(|s| ids.contains(s))); + } + + #[test] + fn agg_plan_arity_and_coverage() { + // Balanced 8-leaf tree: ((0 1)(2 3)) ((4 5)(6 7)). + let t = node( + node(node(leaf(0), leaf(1)), node(leaf(2), leaf(3))), + node(node(leaf(4), leaf(5)), node(leaf(6), leaf(7))), + ); + for arity in [2usize, 3, 4, 8] { + let plan = agg_plan(&t, arity); + // Every Agg has 2..=arity children; the last op is the root Agg. + let mut seen_leaves: Vec = Vec::new(); + for op in &plan { + match op { + FoldOp::Leaf(id) => seen_leaves.push(*id), + FoldOp::Agg(ch) => { + assert!( + ch.len() >= 2 && ch.len() <= arity, + "arity {arity}: {ch:?}" + ); + // children reference earlier entries (post order) + assert!(ch.iter().all(|&i| i < plan.len())); + }, + } + } + assert!(matches!(plan.last(), Some(FoldOp::Agg(_)))); + seen_leaves.sort_unstable(); + assert_eq!(seen_leaves, (0..8).collect::>(), "arity {arity}"); + } + } + + #[test] + fn agg_plan_children_in_shard_order() { + // Children of each agg must be emitted in increasing min-leaf order so the + // subject merkle-fold stays left-associative. + let t = node(node(leaf(0), leaf(1)), node(leaf(2), leaf(3))); + let plan = agg_plan(&t, 2); + // Root folds two subtrees; the left subtree's leaves precede the right's. + if let Some(FoldOp::Agg(ch)) = plan.last() { + // Map each child slot to its min leaf and assert ascending. + let mins: Vec = ch + .iter() + .map(|&i| match &plan[i] { + FoldOp::Leaf(id) => *id, + FoldOp::Agg(g) => g + .iter() + .map( + |&j| if let FoldOp::Leaf(x) = &plan[j] { *x } else { u32::MAX }, + ) + .min() + .unwrap_or(u32::MAX), + }) + .collect(); + let mut sorted = mins.clone(); + sorted.sort_unstable(); + assert_eq!(mins, sorted); + } else { + panic!("root must be an Agg"); + } + } + + #[test] + fn prune_collapses_and_preserves() { + // ((0 1)(2 3)): pruning leaf 1 collapses its parent to leaf 0; pruning a + // whole side collapses the root; keep-all is identity; keep-none is None. + let t = node(node(leaf(0), leaf(1)), node(leaf(2), leaf(3))); + assert_eq!(t.prune(&|_| true), Some(t.clone())); + assert_eq!(t.prune(&|_| false), None); + assert_eq!( + t.prune(&|id| id != 1), + Some(node(leaf(0), node(leaf(2), leaf(3)))) + ); + assert_eq!(t.prune(&|id| id >= 2), Some(node(leaf(2), leaf(3)))); + assert_eq!(t.prune(&|id| id == 3), Some(leaf(3))); + } + + #[test] + fn manifest_tree_roundtrip() { + let p = two_clusters(); + let h = Hypergraph::from_profile(&p); + let (shard_of, tree) = h.partition_with_tree(2, 0.10); + let m = ShardManifest::build(&p, &shard_of, 2).with_tree(tree.clone()); + let bytes = m.to_bytes(); + let q = ShardManifest::from_bytes(&bytes).unwrap(); + assert_eq!(q.tree, Some(tree)); + // A pre-tree manifest (tree = None) still roundtrips. + let m0 = ShardManifest::build(&p, &shard_of, 2); + let q0 = ShardManifest::from_bytes(&m0.to_bytes()).unwrap(); + assert_eq!(q0.tree, None); + } } diff --git a/src/ix/kernel/subst.rs b/crates/kernel/src/subst.rs similarity index 98% rename from src/ix/kernel/subst.rs rename to crates/kernel/src/subst.rs index 04a7dbaf2..58bac969b 100644 --- a/src/ix/kernel/subst.rs +++ b/crates/kernel/src/subst.rs @@ -11,8 +11,6 @@ //! uses a `PtrMap Expr Expr` for the same reason (see //! `refs/lean4lean/Lean4Lean/Expr.lean:14`). -use std::sync::LazyLock; - use rustc_hash::FxHashMap; use super::env::{Addr, InternTable}; @@ -25,8 +23,8 @@ use super::mode::KernelMode; /// seconds per infer call likely has substs dominating. The counter /// only fires for the top-level `subst` entry, so recursive sub-calls /// don't inflate the number. -static IX_SUBST_COUNT_LOG: LazyLock = - LazyLock::new(|| std::env::var("IX_SUBST_COUNT_LOG").is_ok()); +static IX_SUBST_COUNT_LOG: crate::EnvFlag = + crate::EnvFlag::new(|| crate::env_var("IX_SUBST_COUNT_LOG").is_ok()); static SUBST_COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); @@ -53,7 +51,7 @@ pub fn subst( if *IX_SUBST_COUNT_LOG && depth == 0 { let n = SUBST_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed); if n.is_multiple_of(100_000) && n > 0 { - eprintln!("[subst] count={n}"); + log::info!("[subst] count={n}"); } } // Fast path: no loose bound vars at or below `depth` means nothing to @@ -281,7 +279,7 @@ fn simul_subst_cached( cache.insert(key, r.clone()); return r; } else if i >= depth + n { - KExpr::var(i - n, M::meta_field(crate::ix::env::Name::anon())) + KExpr::var(i - n, M::meta_field(ix_common::env::Name::anon())) } else { let r = body.clone(); cache.insert(key, r.clone()); @@ -634,6 +632,10 @@ fn instantiate_rev_cached( depth: u64, cache: &mut FxHashMap<(Addr, u64), KExpr>, ) -> KExpr { + // Profiler: count every substitution-node visit — the work-volume feature + // that `heartbeats` (step count) misses. Records out of circuit; the bump + // compiles out on the zkvm target. + crate::profile::bump_subst_nodes(); // No loose bvars at or below `depth` means nothing to instantiate at // this subtree. if body.lbr() <= depth { @@ -661,7 +663,7 @@ fn instantiate_rev_cached( return r; } else if i >= depth + n { // Free variable above the instantiated range: shift down by `n`. - KExpr::var(i - n, M::meta_field(crate::ix::env::Name::anon())) + KExpr::var(i - n, M::meta_field(ix_common::env::Name::anon())) } else { // i < depth: bound by an inner binder we walked under; unchanged. let r = body.clone(); @@ -780,7 +782,7 @@ fn abstract_fvars_cached( // pass through unchanged (they belong to outer abstractions). if let Some(&p) = pos.get(id) { let new_var = - KExpr::var(depth + p, M::meta_field(crate::ix::env::Name::anon())); + KExpr::var(depth + p, M::meta_field(ix_common::env::Name::anon())); let interned = env.intern_expr(new_var); cache.insert(key, interned.clone()); return interned; @@ -874,11 +876,11 @@ impl ExprData { #[cfg(test)] mod tests { use super::*; - use crate::ix::address::Address; - use crate::ix::kernel::id::KId; - use crate::ix::kernel::level::KUniv; - use crate::ix::kernel::mode::Anon; - use lean_ffi::nat::Nat; + use crate::id::KId; + use crate::level::KUniv; + use crate::mode::Anon; + use bignat::Nat; + use ix_common::address::Address; type AE = KExpr; diff --git a/src/ix/kernel/tc.rs b/crates/kernel/src/tc.rs similarity index 97% rename from src/ix/kernel/tc.rs rename to crates/kernel/src/tc.rs index 3cccfa8d1..92dd1a238 100644 --- a/src/ix/kernel/tc.rs +++ b/crates/kernel/src/tc.rs @@ -6,13 +6,11 @@ //! WHNF, type inference, def-eq, and constant checking are in separate modules //! that add `impl TypeChecker` blocks. -use std::sync::LazyLock; - use rustc_hash::FxHashMap; use rustc_hash::FxHashSet; -use crate::ix::address::Address; -use crate::ix::ixon::env::Env as IxonEnv; +use ix_common::address::Address; +use ixon::env::Env as IxonEnv; use super::constant::{KConst, RecRule}; use super::env::{Addr, KEnv}; @@ -58,15 +56,15 @@ pub const MAX_DEF_EQ_DEPTH: u32 = 2_000; /// for bisecting suspected loops. pub const MAX_REC_FUEL: u64 = 10_000_000; -static IX_MAX_REC_FUEL: LazyLock> = LazyLock::new(|| { - std::env::var("IX_MAX_REC_FUEL").ok().and_then(|s| s.parse().ok()) +static IX_MAX_REC_FUEL: crate::EnvOptU64 = crate::EnvOptU64::new(|| { + crate::env_var("IX_MAX_REC_FUEL").ok().and_then(|s| s.parse().ok()) }); -static IX_HOT_MISSES: LazyLock = - LazyLock::new(|| std::env::var("IX_HOT_MISSES").is_ok()); +static IX_HOT_MISSES: crate::EnvFlag = + crate::EnvFlag::new(|| crate::env_var("IX_HOT_MISSES").is_ok()); -static IX_HOT_MISS_CTX: LazyLock = - LazyLock::new(|| std::env::var("IX_HOT_MISS_CTX").is_ok()); +static IX_HOT_MISS_CTX: crate::EnvFlag = + crate::EnvFlag::new(|| crate::env_var("IX_HOT_MISS_CTX").is_ok()); pub fn max_rec_fuel() -> u64 { (*IX_MAX_REC_FUEL).unwrap_or(MAX_REC_FUEL) @@ -538,8 +536,8 @@ impl<'a, M: KernelMode> TypeChecker<'a, M> { /// the binder scope. pub fn open_binder( &mut self, - name: M::MField, - bi: M::MField, + name: M::MField, + bi: M::MField, ty: KExpr, body: &KExpr, ) -> (KExpr, FVarId) { @@ -559,8 +557,8 @@ impl<'a, M: KernelMode> TypeChecker<'a, M> { ty: KExpr, body: &KExpr, ) -> (KExpr, FVarId) { - let name = M::meta_field(crate::ix::env::Name::anon()); - let bi = M::meta_field(crate::ix::env::BinderInfo::Default); + let name = M::meta_field(ix_common::env::Name::anon()); + let bi = M::meta_field(ix_common::env::BinderInfo::Default); self.open_binder(name, bi, ty, body) } @@ -569,8 +567,8 @@ impl<'a, M: KernelMode> TypeChecker<'a, M> { /// abstract_fvars / structural identity comparisons). pub fn open_binder_with_fv( &mut self, - name: M::MField, - bi: M::MField, + name: M::MField, + bi: M::MField, ty: KExpr, body: &KExpr, ) -> (KExpr, KExpr, FVarId) { @@ -588,8 +586,8 @@ impl<'a, M: KernelMode> TypeChecker<'a, M> { ty: KExpr, body: &KExpr, ) -> (KExpr, KExpr, FVarId) { - let name = M::meta_field(crate::ix::env::Name::anon()); - let bi = M::meta_field(crate::ix::env::BinderInfo::Default); + let name = M::meta_field(ix_common::env::Name::anon()); + let bi = M::meta_field(ix_common::env::BinderInfo::Default); self.open_binder_with_fv(name, bi, ty, body) } @@ -599,7 +597,7 @@ impl<'a, M: KernelMode> TypeChecker<'a, M> { /// for downstream WHNF zeta-reduction). pub fn open_let( &mut self, - name: M::MField, + name: M::MField, ty: KExpr, val: KExpr, body: &KExpr, @@ -617,8 +615,8 @@ impl<'a, M: KernelMode> TypeChecker<'a, M> { /// later or in parallel). Returns the fvar id and the interned fvar /// expression. pub fn push_fvar_decl_anon(&mut self, ty: KExpr) -> (FVarId, KExpr) { - let name = M::meta_field(crate::ix::env::Name::anon()); - let bi = M::meta_field(crate::ix::env::BinderInfo::Default); + let name = M::meta_field(ix_common::env::Name::anon()); + let bi = M::meta_field(ix_common::env::BinderInfo::Default); let fv_id = self.fresh_fvar_id(); let fv = self.intern(KExpr::fvar(fv_id, name.clone())); self.lctx.push(fv_id, LocalDecl::CDecl { name, bi, ty }); @@ -852,7 +850,7 @@ impl<'a, M: KernelMode> TypeChecker<'a, M> { } pub fn debug_label_matches_env(&self) -> bool { - match std::env::var("IX_KERNEL_DEBUG_CONST") { + match crate::env_var("IX_KERNEL_DEBUG_CONST") { Ok(filter) if filter.is_empty() => true, Ok(filter) => { self.debug_label.as_ref().is_some_and(|label| label.contains(&filter)) @@ -865,10 +863,10 @@ impl<'a, M: KernelMode> TypeChecker<'a, M> { #[inline] pub fn tick(&mut self) -> Result<(), TcError> { if self.rec_fuel == 0 { - if std::env::var("IX_REC_FUEL_DUMP").is_ok() + if crate::env_var("IX_REC_FUEL_DUMP").is_ok() && self.debug_label_matches_env() { - eprintln!( + log::info!( "[rec fuel] exhausted const={} depth={} def_eq_depth={} infer_only={} native_reduce={} eager_reduce={}", self.debug_label.as_deref().unwrap_or(""), self.depth(), @@ -878,7 +876,7 @@ impl<'a, M: KernelMode> TypeChecker<'a, M> { self.eager_reduce ); self.dump_hot_misses(); - eprintln!("{}", std::backtrace::Backtrace::force_capture()); + log::info!("{}", std::backtrace::Backtrace::force_capture()); } return Err(TcError::MaxRecFuel); } @@ -907,9 +905,12 @@ impl<'a, M: KernelMode> TypeChecker<'a, M> { // `cur_const` is unset) so producers never leak into the next constant. if self.env.profile_sink.is_some() { let producers = std::mem::take(&mut self.delta_targets); + // Always drain the op counters (even when `cur_const` is unset) so they + // never leak into the next constant, mirroring `delta_targets`. + let ops = crate::profile::take_op_counts(); if let Some(addr) = self.cur_const.take() { let sink = self.env.profile_sink.as_mut().unwrap(); - sink.record(addr, used, producers); + sink.record(addr, used, producers, ops); } } } @@ -928,6 +929,7 @@ impl<'a, M: KernelMode> TypeChecker<'a, M> { /// of `id`. No-op unless a `profile_sink` is installed. #[inline] pub(crate) fn record_delta_target(&mut self, id: &KId) { + crate::perf::record_delta_histo(&id.addr); if self.env.profile_sink.is_some() { self.delta_targets.insert(id.addr.clone()); } @@ -1013,9 +1015,9 @@ impl<'a, M: KernelMode> TypeChecker<'a, M> { } let mut entries: Vec<_> = self.hot_misses.iter().collect(); entries.sort_unstable_by(|a, b| b.1.cmp(a.1).then_with(|| a.0.cmp(b.0))); - eprintln!("[hot misses] top {}:", entries.len().min(25)); + log::info!("[hot misses] top {}:", entries.len().min(25)); for (key, count) in entries.into_iter().take(25) { - eprintln!(" {count:>8} {key}"); + log::info!(" {count:>8} {key}"); } } } @@ -1165,8 +1167,8 @@ mod tests { apps, cnst, mk_addr, mk_id, mk_name, pi, sort0, sort1, uzero, var, }; use super::*; - use crate::ix::address::Address; - use crate::ix::kernel::mode::Meta; + use crate::mode::Meta; + use ix_common::address::Address; fn new_tc() -> TypeChecker<'static, Meta> { let env = Box::leak(Box::new(KEnv::::new())); @@ -1682,7 +1684,7 @@ mod tests { // Deep embedding: λ x. app target (var 0) let e = KExpr::::lam( mk_name("x"), - crate::ix::env::BinderInfo::Default, + ix_common::env::BinderInfo::Default, sort0(), KExpr::app(target, var(0)), ); diff --git a/src/ix/kernel/testing.rs b/crates/kernel/src/testing.rs similarity index 97% rename from src/ix/kernel/testing.rs rename to crates/kernel/src/testing.rs index 2fa6e9c9e..8c29a274d 100644 --- a/src/ix/kernel/testing.rs +++ b/crates/kernel/src/testing.rs @@ -3,9 +3,9 @@ //! Provides convenience constructors for `KExpr`, `KUniv`, `KId`, //! and `KConst` to reduce boilerplate in hand-built test environments. -use crate::ix::address::Address; -use crate::ix::env::{BinderInfo, DefinitionSafety, Name, ReducibilityHints}; -use crate::ix::ixon::constant::DefKind; +use ix_common::address::Address; +use ix_common::env::{BinderInfo, DefinitionSafety, Name, ReducibilityHints}; +use ixon::constant::DefKind; use super::constant::KConst; use super::env::KEnv; diff --git a/src/ix/kernel/tutorial/basic.rs b/crates/kernel/src/tutorial/basic.rs similarity index 99% rename from src/ix/kernel/tutorial/basic.rs rename to crates/kernel/src/tutorial/basic.rs index e460fdd23..adbc562d8 100644 --- a/src/ix/kernel/tutorial/basic.rs +++ b/crates/kernel/src/tutorial/basic.rs @@ -3,10 +3,10 @@ #[cfg(test)] mod tests { - use crate::ix::env::ReducibilityHints; - use crate::ix::kernel::env::KEnv; - use crate::ix::kernel::mode::Meta; - use crate::ix::kernel::testing::*; + use crate::env::KEnv; + use crate::mode::Meta; + use crate::testing::*; + use ix_common::env::ReducibilityHints; // ========================================================================== // Batch 1: Basic definitions (Tutorial.lean lines 16–60) diff --git a/src/ix/kernel/tutorial/defeq.rs b/crates/kernel/src/tutorial/defeq.rs similarity index 98% rename from src/ix/kernel/tutorial/defeq.rs rename to crates/kernel/src/tutorial/defeq.rs index 4c09634bf..b97f9ee72 100644 --- a/src/ix/kernel/tutorial/defeq.rs +++ b/crates/kernel/src/tutorial/defeq.rs @@ -3,11 +3,11 @@ #[cfg(test)] mod tests { - use crate::ix::env::Name; - use crate::ix::kernel::constant::{KConst, RecRule}; - use crate::ix::kernel::env::KEnv; - use crate::ix::kernel::mode::Meta; - use crate::ix::kernel::testing::*; + use crate::constant::{KConst, RecRule}; + use crate::env::KEnv; + use crate::mode::Meta; + use crate::testing::*; + use ix_common::env::Name; // ========================================================================== // Batch 4: Proof irrelevance and eta (Tutorial.lean lines 953–1013) @@ -50,7 +50,7 @@ mod tests { vec![], ty, val, - crate::ix::env::ReducibilityHints::Abbrev, + ix_common::env::ReducibilityHints::Abbrev, ); env.insert(id.clone(), c); check_accepts(&mut env, &id); @@ -811,7 +811,7 @@ mod tests { // Value: fun α r a h p => Eq.refl p (BOGUS — claims reduction happened) let val = ME::lam( mk_name("α"), - crate::ix::env::BinderInfo::Implicit, + ix_common::env::BinderInfo::Implicit, sort1(), nlam( "r", @@ -1069,11 +1069,11 @@ mod tests { let minor_r = app(app(var(0), var(1)), eq_refl_r); let rule_rhs = ME::lam( mk_name("α"), - crate::ix::env::BinderInfo::Implicit, + ix_common::env::BinderInfo::Implicit, sort(param(1)), ME::lam( mk_name("a"), - crate::ix::env::BinderInfo::Implicit, + ix_common::env::BinderInfo::Implicit, var(0), nlam("motive", motive_ty_r, nlam("refl", minor_r, var(0))), ), @@ -1396,7 +1396,7 @@ mod tests { vec![], ty, val, - crate::ix::env::ReducibilityHints::Opaque, + ix_common::env::ReducibilityHints::Opaque, ); env.insert(id.clone(), c); check_rejects(&mut env, &id); @@ -1499,7 +1499,7 @@ mod tests { vec![], ty, val, - crate::ix::env::ReducibilityHints::Opaque, + ix_common::env::ReducibilityHints::Opaque, ); env.insert(id.clone(), c); check_rejects(&mut env, &id); @@ -1523,11 +1523,11 @@ mod tests { // fun {a} {b} (h : And a b) => .proj And 0 h let val = ME::lam( mk_name("a"), - crate::ix::env::BinderInfo::Implicit, + ix_common::env::BinderInfo::Implicit, sort0(), ME::lam( mk_name("b"), - crate::ix::env::BinderInfo::Implicit, + ix_common::env::BinderInfo::Implicit, sort0(), nlam("h", and_ab, ME::prj(mk_id("And"), 0, var(0))), ), @@ -1539,7 +1539,7 @@ mod tests { vec![], ty, val, - crate::ix::env::ReducibilityHints::Abbrev, + ix_common::env::ReducibilityHints::Abbrev, ); env.insert(id.clone(), c); check_accepts(&mut env, &id); @@ -1554,11 +1554,11 @@ mod tests { let val = ME::lam( mk_name("a"), - crate::ix::env::BinderInfo::Implicit, + ix_common::env::BinderInfo::Implicit, sort0(), ME::lam( mk_name("b"), - crate::ix::env::BinderInfo::Implicit, + ix_common::env::BinderInfo::Implicit, sort0(), nlam("h", and_ab, ME::prj(mk_id("And"), 1, var(0))), ), @@ -1570,7 +1570,7 @@ mod tests { vec![], ty, val, - crate::ix::env::ReducibilityHints::Abbrev, + ix_common::env::ReducibilityHints::Abbrev, ); env.insert(id.clone(), c); check_accepts(&mut env, &id); @@ -1903,7 +1903,7 @@ mod tests { vec![], ty, val, - crate::ix::env::ReducibilityHints::Opaque, + ix_common::env::ReducibilityHints::Opaque, ); env.insert(id.clone(), c); id @@ -2071,7 +2071,7 @@ mod tests { vec![], ty, val, - crate::ix::env::ReducibilityHints::Opaque, + ix_common::env::ReducibilityHints::Opaque, ); env.insert(id.clone(), c); check_rejects(&mut env, &id); @@ -2279,7 +2279,7 @@ mod tests { vec![], ty, val, - crate::ix::env::ReducibilityHints::Opaque, + ix_common::env::ReducibilityHints::Opaque, ); env.insert(id.clone(), c); check_rejects(&mut env, &id); diff --git a/src/ix/kernel/tutorial/inductive.rs b/crates/kernel/src/tutorial/inductive.rs similarity index 99% rename from src/ix/kernel/tutorial/inductive.rs rename to crates/kernel/src/tutorial/inductive.rs index 08df97319..67591714a 100644 --- a/src/ix/kernel/tutorial/inductive.rs +++ b/crates/kernel/src/tutorial/inductive.rs @@ -3,11 +3,11 @@ #[cfg(test)] mod tests { - use crate::ix::env::{Name, ReducibilityHints}; - use crate::ix::kernel::constant::{KConst, RecRule}; - use crate::ix::kernel::env::KEnv; - use crate::ix::kernel::mode::Meta; - use crate::ix::kernel::testing::*; + use crate::constant::{KConst, RecRule}; + use crate::env::KEnv; + use crate::mode::Meta; + use crate::testing::*; + use ix_common::env::{Name, ReducibilityHints}; // ========================================================================== // Batch 3: Bad inductives (Tutorial.lean lines 247–610) diff --git a/src/ix/kernel/tutorial/mod.rs b/crates/kernel/src/tutorial/mod.rs similarity index 100% rename from src/ix/kernel/tutorial/mod.rs rename to crates/kernel/src/tutorial/mod.rs diff --git a/src/ix/kernel/tutorial/reduction.rs b/crates/kernel/src/tutorial/reduction.rs similarity index 98% rename from src/ix/kernel/tutorial/reduction.rs rename to crates/kernel/src/tutorial/reduction.rs index e57e4ae72..aa4822015 100644 --- a/src/ix/kernel/tutorial/reduction.rs +++ b/crates/kernel/src/tutorial/reduction.rs @@ -3,12 +3,12 @@ #[cfg(test)] mod tests { - use crate::ix::env::{Name, ReducibilityHints}; - use crate::ix::kernel::constant::KConst; - use crate::ix::kernel::constant::RecRule; - use crate::ix::kernel::env::KEnv; - use crate::ix::kernel::mode::Meta; - use crate::ix::kernel::testing::*; + use crate::constant::KConst; + use crate::constant::RecRule; + use crate::env::KEnv; + use crate::mode::Meta; + use crate::testing::*; + use ix_common::env::{Name, ReducibilityHints}; // ========================================================================== // Batch 5: Peano arithmetic (Tutorial.lean lines 127–153) @@ -985,8 +985,8 @@ mod tests { // The zero kernel's infer_nat_type uses prims.nat to construct the type. // We use N as our Nat, so we need prims.nat = mk_id("N"). // aNatLit : N := NatVal(0) - use crate::ix::address::Address; - use lean_ffi::nat::Nat; + use bignat::Nat; + use ix_common::address::Address; let nat_0 = ME::nat(Nat::from(0u64), Address::hash(b"natval_0")); let (id, c) = mk_defn("aNatLit", 0, vec![], nat(), nat_0, ReducibilityHints::Opaque); @@ -1005,8 +1005,8 @@ mod tests { let mut env = nat_env(); let nat = || cnst("N", &[]); - use crate::ix::address::Address; - use lean_ffi::nat::Nat; + use bignat::Nat; + use ix_common::address::Address; let nat_3 = ME::nat(Nat::from(3u64), Address::hash(b"natval_3")); let succ_succ_succ_zero = app( @@ -1214,11 +1214,11 @@ mod tests { // mk_case fst snd = app(app(var(2), var(1)), var(0)) let rule_rhs = ME::lam( mk_name("α"), - crate::ix::env::BinderInfo::Implicit, + ix_common::env::BinderInfo::Implicit, sort(usucc(param(0))), ME::lam( mk_name("β"), - crate::ix::env::BinderInfo::Implicit, + ix_common::env::BinderInfo::Implicit, sort(usucc(param(1))), nlam( "motive", @@ -1468,7 +1468,7 @@ mod tests { let mut env = KEnv::::new(); add_eq_inductive(&mut env); - use crate::ix::env::QuotKind; + use ix_common::env::QuotKind; // Quot.{u} : {α : Sort u} → (α → α → Prop) → Sort u // depth 1 (inside α): α = var(0) @@ -1627,9 +1627,7 @@ mod tests { env } - fn quot_prims( - env: &KEnv, - ) -> crate::ix::kernel::primitive::Primitives { + fn quot_prims(env: &KEnv) -> crate::primitive::Primitives { let mut prims = test_prims(env); prims.quot_type = mk_id("Quot"); prims.quot_ctor = mk_id("Quot.mk"); diff --git a/src/ix/kernel/whnf.rs b/crates/kernel/src/whnf.rs similarity index 97% rename from src/ix/kernel/whnf.rs rename to crates/kernel/src/whnf.rs index 16d74f4db..0ca940abe 100644 --- a/src/ix/kernel/whnf.rs +++ b/crates/kernel/src/whnf.rs @@ -6,22 +6,22 @@ use std::sync::LazyLock; use rustc_hash::FxHashSet; -use crate::ix::address::Address; -use crate::ix::ixon::constant::DefKind; +use ix_common::address::Address; +use ixon::constant::DefKind; /// When set, emit a `[iota stuck]` line whenever `try_iota` can't resolve /// its major premise to a constructor. Set `IX_IOTA_STUCK=1` to activate /// and optionally pass a substring filter (e.g. `IX_IOTA_STUCK=Poly.rec`) /// to suppress recursor-unrelated noise. -static IX_IOTA_STUCK: LazyLock> = - LazyLock::new(|| std::env::var("IX_IOTA_STUCK").ok()); +static IX_IOTA_STUCK: crate::EnvString = + crate::EnvString::new(|| crate::env_var("IX_IOTA_STUCK").ok()); /// When set, log total `nat_to_constructor` calls every 100k. Lets us see /// whether a given check is doing runaway Nat iota expansion (signalling /// a `Nat.rec motive base step N` whose step unconditionally forces `ih` /// \u2014 the pattern the old 2^20 threshold guarded against). -static IX_NAT_EXPAND_LOG: LazyLock = - LazyLock::new(|| std::env::var("IX_NAT_EXPAND_LOG").is_ok()); +static IX_NAT_EXPAND_LOG: crate::EnvFlag = + crate::EnvFlag::new(|| crate::env_var("IX_NAT_EXPAND_LOG").is_ok()); /// Global counter for `nat_to_constructor` calls. Read lazily via /// `IX_NAT_EXPAND_LOG`. `fetch_add(_, Relaxed)` is a near-free no-op when @@ -29,34 +29,34 @@ static IX_NAT_EXPAND_LOG: LazyLock = static NAT_EXPAND_COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); -static IX_NAT_IOTA_TRACE: LazyLock = - LazyLock::new(|| std::env::var("IX_NAT_IOTA_TRACE").is_ok()); +static IX_NAT_IOTA_TRACE: crate::EnvFlag = + crate::EnvFlag::new(|| crate::env_var("IX_NAT_IOTA_TRACE").is_ok()); static NAT_IOTA_TRACE_COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); -static IX_NAT_LINEAR_REC_TRACE: LazyLock = - LazyLock::new(|| std::env::var("IX_NAT_LINEAR_REC_TRACE").is_ok()); +static IX_NAT_LINEAR_REC_TRACE: crate::EnvFlag = + crate::EnvFlag::new(|| crate::env_var("IX_NAT_LINEAR_REC_TRACE").is_ok()); static NAT_LINEAR_REC_TRACE_COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); /// When set, log every 1M whnf entries. A check using tens of millions /// of whnf calls on a single constant is deep in pathological territory. -static IX_WHNF_COUNT_LOG: LazyLock = - LazyLock::new(|| std::env::var("IX_WHNF_COUNT_LOG").is_ok()); +static IX_WHNF_COUNT_LOG: crate::EnvFlag = + crate::EnvFlag::new(|| crate::env_var("IX_WHNF_COUNT_LOG").is_ok()); static WHNF_COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); -static IX_DELTA_TRACE: LazyLock> = - LazyLock::new(|| std::env::var("IX_DELTA_TRACE").ok()); +static IX_DELTA_TRACE: crate::EnvString = + crate::EnvString::new(|| crate::env_var("IX_DELTA_TRACE").ok()); -static IX_PROJ_TRACE: LazyLock> = - LazyLock::new(|| std::env::var("IX_PROJ_TRACE").ok()); +static IX_PROJ_TRACE: crate::EnvString = + crate::EnvString::new(|| crate::env_var("IX_PROJ_TRACE").ok()); -static IX_NAT_TRACE: LazyLock> = - LazyLock::new(|| std::env::var("IX_NAT_TRACE").ok()); +static IX_NAT_TRACE: crate::EnvString = + crate::EnvString::new(|| crate::env_var("IX_NAT_TRACE").ok()); const NAT_REDUCER_OPEN_ARG_REC_FUEL: u64 = 4096; @@ -69,7 +69,7 @@ use super::mode::KernelMode; use super::subst::{simul_subst, subst, subst_no_intern}; use super::tc::{IotaInfo, MAX_WHNF_FUEL, TypeChecker, collect_app_spine}; -use lean_ffi::nat::Nat; +use bignat::Nat; /// Reduction policy for structural WHNF. /// @@ -116,14 +116,14 @@ impl TypeChecker<'_, M> { original: &KExpr, current: &KExpr, ) { - if std::env::var("IX_WHNF_FUEL_DUMP").is_err() + if crate::env_var("IX_WHNF_FUEL_DUMP").is_err() || !self.debug_label_matches_env() { return; } let (orig_head, orig_args) = collect_app_spine(original); let (cur_head, cur_args) = collect_app_spine(current); - eprintln!( + log::info!( "[whnf fuel] {phase} const={} depth={} original_head={} original_args={} current_head={} current_args={}", self.debug_label.as_deref().unwrap_or(""), self.depth(), @@ -132,8 +132,8 @@ impl TypeChecker<'_, M> { cur_head, cur_args.len() ); - eprintln!(" original: {original}"); - eprintln!(" current: {current}"); + log::info!(" original: {original}"); + log::info!(" current: {current}"); } fn dump_delta_trace(&self, id: &KId, arity: usize, e: &KExpr) { @@ -147,7 +147,7 @@ impl TypeChecker<'_, M> { if !filter.is_empty() && !id_s.contains(filter) { return; } - eprintln!( + log::info!( "[delta] const={} depth={} head={} args={arity} expr={}", self.debug_label.as_deref().unwrap_or(""), self.depth(), @@ -176,7 +176,7 @@ impl TypeChecker<'_, M> { } let (head, args) = collect_app_spine(wval); match result { - Some(result) => eprintln!( + Some(result) => log::info!( "[proj] const={} depth={} proj={} field={} struct_head={} struct_args={} ctor_params={:?} result={}", self.debug_label.as_deref().unwrap_or(""), self.depth(), @@ -187,7 +187,7 @@ impl TypeChecker<'_, M> { ctor_params, result ), - None => eprintln!( + None => log::info!( "[proj] const={} depth={} proj={} field={} struct_head={} struct_args={} ctor_params={:?} result=", self.debug_label.as_deref().unwrap_or(""), self.depth(), @@ -212,7 +212,7 @@ impl TypeChecker<'_, M> { if !filter.is_empty() && !head_s.contains(filter) { return; } - eprintln!( + log::info!( "[nat] const={} depth={} phase={} head={} args={} expr={}", self.debug_label.as_deref().unwrap_or(""), self.depth(), @@ -236,9 +236,10 @@ impl TypeChecker<'_, M> { if *IX_WHNF_COUNT_LOG { let n = WHNF_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed); if n.is_multiple_of(100_000) && n > 0 { - eprintln!("[whnf] count={n}"); + log::info!("[whnf] count={n}"); } } + crate::profile::bump_whnf(); // Quick exit for non-reducing forms. match e.data() { ExprData::Sort(..) @@ -323,7 +324,7 @@ impl TypeChecker<'_, M> { } // String literal primitives such as `String.back ""`. - if let Some(reduced) = self.try_reduce_string(&cur)? { + if let Some(reduced) = self.try_reduce_string(&cur) { cur = reduced; continue; } @@ -721,7 +722,7 @@ impl TypeChecker<'_, M> { } // String literal primitives. - if let Some(reduced) = self.try_reduce_string(&cur)? { + if let Some(reduced) = self.try_reduce_string(&cur) { cur = reduced; continue; } @@ -934,7 +935,7 @@ impl TypeChecker<'_, M> { let n = NAT_IOTA_TRACE_COUNT .fetch_add(1, std::sync::atomic::Ordering::Relaxed); if n < 32 { - eprintln!( + log::info!( "[nat_iota_trace] rec={} major_bits={} spine={} major_idx={}", rec_id, val.0.bits(), @@ -978,13 +979,14 @@ impl TypeChecker<'_, M> { if !is_ctor && let Some(filter) = IX_IOTA_STUCK.as_ref() { let rec_name = format!("{rec_id}"); if filter.is_empty() || rec_name.contains(filter) { - eprintln!("[iota stuck] rec={rec_name}"); - eprintln!("[iota stuck] major: {major}"); - eprintln!("[iota stuck] major whnf: {major_whnf}"); + log::info!("[iota stuck] rec={rec_name}"); + log::info!("[iota stuck] major: {major}"); + log::info!("[iota stuck] major whnf: {major_whnf}"); } } if is_ctor { + crate::perf::record_iota_histo(&rec_id.addr); let ctor_id = match ctor_head.data() { ExprData::Const(id, _, _) => id, _ => unreachable!(), @@ -1672,7 +1674,7 @@ impl TypeChecker<'_, M> { let n = NAT_EXPAND_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed); if n.is_multiple_of(10_000) { - eprintln!("[nat_to_constructor] count={n} val_bits={}", val.0.bits()); + log::info!("[nat_to_constructor] count={n} val_bits={}", val.0.bits()); } } if val.0 == BigUint::ZERO { @@ -1792,6 +1794,18 @@ impl TypeChecker<'_, M> { &mut self, arg: &KExpr, ) -> Result>, TcError> { + // Stuck-chain memo. The inner WHNF below runs in `NatSuccMode::Stuck`, + // which bypasses the WHNF caches, so without this memo a stuck + // `succ^k(x)` chain is re-peeled in full from every depth it is + // encountered at — O(k²) fuel for `x + literal` on a symbolic `x`. + // Every suffix of a stuck chain is itself stuck, so on the stuck exit + // all visited keys are recorded. + let entry_key = self.whnf_key(arg); + if self.env.nat_succ_stuck.contains(&entry_key) { + return Ok(None); + } + let mut visited: Vec<(super::env::Addr, super::env::Addr)> = + vec![entry_key]; let mut offset = num_bigint::BigUint::from(1u64); let mut cur = arg.clone(); @@ -1814,11 +1828,24 @@ impl TypeChecker<'_, M> { && id.addr == self.prims.nat_succ.addr && args.len() == 1 { + crate::perf::record_nat_succ_peel(); offset += 1u64; cur = args[0].clone(); + let cur_key = self.whnf_key(&cur); + if self.env.nat_succ_stuck.contains(&cur_key) { + // Known-stuck suffix: the whole chain above it is stuck too. + self.env.nat_succ_stuck.extend(visited); + return Ok(None); + } + visited.push(cur_key); + // The whnf'd form `succ(cur)` can also surface later as a + // succ-iter argument; record it alongside the raw chain. + let w_key = self.whnf_key(&w); + visited.push(w_key); continue; } + self.env.nat_succ_stuck.extend(visited); return Ok(None); } } @@ -1842,7 +1869,7 @@ impl TypeChecker<'_, M> { .fetch_add(1, std::sync::atomic::Ordering::Relaxed); if n < 8 { let step_whnf = self.whnf(step)?; - eprintln!( + log::info!( "[nat_linear_rec] major_bits={} base_idx={} step_idx={} spine={} step_whnf={}", parts.major.0.bits(), parts.base_idx, @@ -2756,46 +2783,43 @@ impl TypeChecker<'_, M> { // String primitive reduction // ----------------------------------------------------------------------- - pub(super) fn try_reduce_string( - &mut self, - e: &KExpr, - ) -> Result>, TcError> { + pub(super) fn try_reduce_string(&mut self, e: &KExpr) -> Option> { let (head, args) = collect_app_spine(e); if args.len() != 1 { - return Ok(None); + return None; } let ExprData::Const(id, _, _) = head.data() else { - return Ok(None); + return None; }; let is_back = id.addr == self.prims.string_back.addr || id.addr == self.prims.string_legacy_back.addr; let is_utf8_byte_size = id.addr == self.prims.string_utf8_byte_size.addr; let is_to_byte_array = id.addr == self.prims.string_to_byte_array.addr; if !is_back && !is_utf8_byte_size && !is_to_byte_array { - return Ok(None); + return None; } let s = match args[0].data() { ExprData::Str(s, _, _) => s, - _ => return Ok(None), + _ => return None, }; if is_utf8_byte_size { let n = Nat::from(s.len() as u64); let addr = Address::hash(&n.to_le_bytes()); - return Ok(Some(self.intern(KExpr::nat(n, addr)))); + return Some(self.intern(KExpr::nat(n, addr))); } if is_to_byte_array { if s.is_empty() { - return Ok(Some(self.intern(KExpr::cnst( + return Some(self.intern(KExpr::cnst( self.prims.byte_array_empty.clone(), Box::new([]), - )))); + ))); } - return Ok(None); + return None; } let codepoint = s.chars().last().map_or(65u32, u32::from); - Ok(Some(self.char_of_nat_expr(u64::from(codepoint)))) + Some(self.char_of_nat_expr(u64::from(codepoint))) } fn char_of_nat_expr(&mut self, n: u64) -> KExpr { @@ -2886,6 +2910,27 @@ fn compute_nat_bin( ) -> Option { use num_bigint::BigUint; let zero = BigUint::ZERO; + // Profiling: charge big-Nat arithmetic limb-work to the `nat_arith` counter, + // which tracks the Aiur `klimbs_*`/`u64_*` circuits (cost ∝ operand limbs; + // ~quadratic for mul/div/mod/gcd, linear for add/sub/bitwise/shift). Invisible + // to hb/subst/def_eq, so it's the missing feature for FFT-cost prediction. + { + let la = (a.0.bits().max(1)).div_ceil(64); + let lb = (b.0.bits().max(1)).div_ceil(64); + let work = if *addr == p.nat_mul.addr + || *addr == p.nat_div.addr + || *addr == p.nat_mod.addr + || *addr == p.nat_gcd.addr + { + la.saturating_mul(lb) + } else if *addr == p.nat_pow.addr { + let lo = la.max(lb); + lo.saturating_mul(lo) + } else { + la.max(lb) + }; + crate::profile::bump_nat_arith(work); + } let r = if *addr == p.nat_add.addr { &a.0 + &b.0 } else if *addr == p.nat_sub.addr { @@ -3027,9 +3072,9 @@ mod tests { use super::super::primitive::Primitives; use super::super::tc::TypeChecker; use super::*; - use crate::ix::address::Address; - use crate::ix::env::{DefinitionSafety, ReducibilityHints}; - use crate::ix::ixon::constant::DefKind; + use ix_common::address::Address; + use ix_common::env::{DefinitionSafety, ReducibilityHints}; + use ixon::constant::DefKind; type AE = KExpr; type AU = KUniv; @@ -4151,7 +4196,7 @@ mod tests { }, ExprData::App(..) => { // Might be Nat.succ chain — that's also acceptable - eprintln!("Nat.rec result is App chain (not folded to literal)"); + log::info!("Nat.rec result is App chain (not folded to literal)"); }, other => panic!("unexpected Nat.rec result: {:?}", other), } diff --git a/docs/zisk-cycle-cost-model.md b/docs/zisk-cycle-cost-model.md new file mode 100644 index 000000000..04e7a1927 --- /dev/null +++ b/docs/zisk-cycle-cost-model.md @@ -0,0 +1,332 @@ +# Zisk cycle cost model + +Predicts **Zisk guest cycles** (≈ RISC-V STEPS) and **prover RAM** for +typechecking a Lean environment — or one constant, or a shard — in the Ix zkVM. +Purpose: **shard sizing**, splitting a large environment into pieces that each +prove within the RAM cap while minimizing the number of pieces. + +Measured on an RTX PRO 6000 (250 GiB host). Inputs, scripts, and raw data: +`~/benchdata/prof/`. + +--- + +## Background + +A compiled Lean program is an *environment* (`.ixe`): a set of *constants*. +Typechecking verifies them. The kernel runs from one Rust source both **out of +circuit** (native — used to *profile*) and **in circuit** (RISC-V, inside Zisk — +what gets *proven*). A large env can't prove in one piece, so it is split into +**shards** (planner: `crates/kernel/src/shard.rs`). The constants assigned to a +shard are its **members**; the constants they depend on may live in other shards. + +The profiler (`crates/kernel/src/profile.rs`) records cheap per-constant +variables (the model's inputs) to predict in-circuit cost: +- **hb** (heartbeats) — kernel reduction steps (recursion fuel). +- **bytes** — serialized constant size (deserialization cost). +- **subst** — substitution-node visits (`instantiate_rev`); a finer measure of + reduction *volume* than hb. + +**STEPS vs COST — two different things:** +- **STEPS** = RISC-V instruction count → drives **prover RAM** (trace rows). + This is what the model predicts; 1 step = 1 instruction, workload-independent. +- **COST** = Zisk's weighted proof-area → drives **prove time**. + `COST = 293.6M base + 68·STEPS + opcodes + precompiles + memory`. + **COST/STEPS is workload-dependent**: ≈100 for the Ix kernel, but ≈384 for a + precompile-heavy blake3 program (its blake3 precompile is 67% of cost at almost + no steps). So STEPS→RAM is portable; STEPS→prove-time needs a per-workload + factor. + +**What actually drives STEPS** (verified in the Zisk + kernel source). One +RISC-V instruction = exactly 1 step (`zisk .../emu.rs`), so cycles are the +kernel's total instruction count, and the kernel's instructions are +overwhelmingly **reduction** — `whnf` + `is_def_eq`. One heartbeat is one +reduction step past the memo caches (`tc.rs`, `whnf.rs`, `def_eq.rs`); `hb` +counts those steps, `subst` counts the substitution-DAG traversal inside them. +Three things are deliberately **not** step drivers, which is why the model +ignores them: +- **blake3 hashing** is a Zisk precompile — 1 step, 12,000 COST. A COST driver, + not a STEP driver. +- **Memory traffic** is a COST term (`MEM_READ_COST` etc.), not steps. +- **Serialized literal content** (strings, byte blobs) is stored by reference in + a separate `blobs` map and fetched lazily — **never walked into terms**. This + is the mechanistic reason `bytes` doesn't predict cycles and a 1 MB string + literal is ~free (confirmed both in source and by direct measurement). + +--- + +## Cost models + +For a shard, sum the variables over **only its own member constants** — not the +dependency closures they pull in (see Findings). + +Fit quality is reported as **MAPE** (mean absolute percentage error — the average +of `|predicted − actual| / actual`, so "6%" means predictions land within ~6% of +measured cycles on average) and **R²** (fraction of variance explained, 1.0 = +perfect). Fits are weighted least squares minimizing *relative* error, the right +objective for a cost spanning 50M–9B cycles. + +**Full-closure typecheck** — a self-contained closure: a small program, or a +constant checked with all its dependencies. Calibrated **cross-library** on +n=76: 12 small programs + 64 diverse constants checked full-closure via +`--constant`, spanning **Init (51), Std (3), and Mathlib (10)**: +``` +cycles ≈ 0.68M + 96,989·hb + 4,151·subst R² 0.987, MAPE 6% +``` +Per-library MAPE is uniform (Init 6%, Std 6%, Mathlib 5%, programs 8%) — one +model across libraries, not Init-tuned. +Reduction-dominated: `hb` alone reaches R² 0.99, `subst` is the useful second +variable, serialized `bytes` adds nothing. + +**Per shard** — summed over the shard's member constants: +``` +cycles ≈ 199M + 167,900·hb + 2,928·bytes R² 0.974, MAPE 14% + ≈ 180M + 162,300·hb + 652·bytes + 4,276·subst R² 0.988, MAPE 9% +``` +Same slopes as the full-closure model; the extra ~180M intercept is the +per-shard fixed floor plus the foreign-dependency ingress a shard re-pays. The +planner uses exactly these best-fit constants (`shard.rs`: `STEPS_PER_HEARTBEAT` +162,339, `STEPS_PER_SUBST` 4,276, `STEPS_PER_INGRESS_BYTE` 652, +`SHARD_STEP_FLOOR` 180M) — one definition, used for the `ix profile` breakdown, +per-shard prediction, and the packer's cap test. No conservatism is baked into +the slopes (that would distort packing); the cap's margin is `RAM_USABLE_FRAC` +plus the model's own ~1.1× over-prediction. + +**Subject-only** (`--skip-deps`) — check one constant, trust its deps. A +separate, cheaper, **ingress-dominated** regime (`hb` near-useless): +``` +cycles ≈ 0.39M + 6,740·block_bytes + 14·subenv_bytes + 4,070·subst R² 0.96, n=36 +``` +Reserved for constants too expensive to full-closure-check that can't be sharded +(Finding 4). + +**Measuring one constant.** `zisk-host --execute --ixe initstd.ixe --constant +` resolves the name, builds just its closure sub-env (deps lazily faulted +in — no separate `.ixe`, no whole-env ingress), and checks it. Full-closure by +default (`Ix.Claim.check addr none`); add `--skip-deps` for subject-only. Same +flags as the Aiur `bench-typecheck`. + +--- + +## Prover RAM and prove time + +Measured proving 7 Init shards on GPU at `--max-witness-stored 5`, STEPS +0.27–3.79e9: +``` +peak host RAM (GiB) ≈ 50 + 33·STEPS_billions R² 0.99 +GPU prove time (s) ≈ 54 + 158·STEPS_billions R² 0.98 (~6.3M steps/s) +``` +RAM scales with `--max-witness-stored N` (this is N=5). Inverting the RAM model +at `RAM_USABLE_FRAC` = 0.85 gives a safe per-shard cap of **~3.6e9 steps** for a +200 GiB target (`shard.rs:cycle_cap_for_ram`). + +**Validated end-to-end.** Proving + aggregating all of `mergesort` +(an 8.94e9-cycle env) under a ~180 GiB cap produced 3 shards (actual +2.77/2.45/2.20e9 cycles), **147 GiB peak RAM** (predicted ~153), folding to one +unconditional proof in **~23.5 min** (1281 s leaf proving + 13 s aggregation). +Per-shard actuals landed **~1.13× under** the model's prediction and peak RAM +matched — the cap and RAM models hold against real proving, on the conservative +side. + +--- + +## Key findings + +1. **Full-closure cost is reduction-dominated.** `hb + subst` predicts it + (R² 0.99); serialized `bytes` adds nothing. Subject-only cost is the opposite + — ingress-dominated, with `hb` near-useless. +2. **Cost a shard from its members only.** Sum the variables of the shard's + *own* member constants (R² ≈0.95). Including their dependency closures — deps + once, or deps per member — scores **negative R²**: it double-counts shared + deps nonlinearly. So there is no shared-dep reuse to over-provision for. +3. **Sharding is bin-packing per-constant costs** under the RAM cap, *not* + balancing. The cap planner (`partition_for_cycle_cap`) packs each shard as + full as the cap allows — fewest shards (`≈⌈total/cap⌉`), each maximally + performant — rather than equalizing per-shard work (which over-shards: every + shard left partly empty). A fine min-cut pre-partition supplies a cut-coherent + order so dependency overlap packs into the same shard (paid once, not + re-ingressed). The cap test counts that cross-ingress, so a shard packed to + the cap still executes under it. Each leaf pays the ~180M fixed floor and adds + an aggregation node, so cheap constants are batched rather than proven one at + a time. (`--shards N` still does balanced bisection, for manual control.) +4. **A few constants can't be full-closure-proven on a 250 GiB box and aren't + shardable** (single atomic constants): `Vector/Array.extract_append._proof_1`, + the `instRxcHasSize_eq` family. The escape hatch is `--skip-deps`: + `Vector.extract_append` is the 143 GiB OOM case under full-closure but checks + subject-only in 74M cycles. The planner flags these via + `infeasible_atomic_floor`. +5. **The packing order comes from min-cut.** Whole-env profiling of + Init/Std/**Mathlib** (mathlib = 631k blocks) shows Lean typecheck is uniformly + reduction-dominated: own-bytes is only **2.6–7% of member cost** (mathlib + lowest, at 2.6%). On Zisk that makes cross-ingress a ≤3% term, and a plain + block-id order packs within ~2% of min-cut at ~1× the proving cost. min-cut + remains the default because the same `.ixprof`/`.ixes` feeds **Aiur** + (ingress-first), because it stays robust if an ingress-heavy env shifts that + balance, and because it keeps each shard's injected closure under the 512 MB + guest heap. The cap planner is order-agnostic, so the order is a pluggable + choice; min-cut is the one that holds across both backends. + +--- + +## Init coverage + +Applying the cost model to every Init constant's native profile (51,003 blocks / +51,678 constants) vs a single-leaf cap: **~99.98% of Init typechecks on Zisk.** +Exactly **12 constants** exceed a single 250 GiB leaf (`--max-witness-stored 5`) +— all single atomic constants (un-shardable), `hb`-dominated. Listed by name +(all are `_private` proof terms; private prefix elided): + +| constant | hb | pred. steps | +|---|--:|--:| +| `Int16.instRxcHasSize_eq` | 32,843 | 10.5e9 | +| `Int64.instRxcHasSize_eq` | 32,843 | 10.5e9 | +| `Int32.instRxcHasSize_eq` | 32,843 | 10.5e9 | +| `Vector.extract_append._proof_1` | 20,309 | 7.0e9 | +| `Array.extract_append._proof_1_1` | 20,210 | 6.9e9 | +| `Char.succ?_eq` | 18,226 | 5.9e9 | +| `Char.ofOrdinal_le_of_le` | 17,877 | 5.7e9 | +| `Vector.extract_extract._proof_1` | 16,258 | 5.7e9 | +| `ISize.instRxcHasSize_eq` | 16,425 | 5.3e9 | +| `Vector.extract_append_extract._proof_1` | 13,335 | 4.7e9 | +| `Array.extract_append_extract._proof_1_1` | 13,226 | 4.7e9 | +| `Char.ofOrdinal_ordinal` | 14,136 | 4.5e9 | + +Each has a workaround — lower `--max-witness-stored`, a bigger box, `--skip-deps` +(subject-only), or upstream proof restructuring — so 12 is an upper bound on +truly-stuck constants. At a 200 GiB cap the list grows to 16. Estimate uses the +planner's `block_step_cost` model (`162,339·hb + 4,276·subst + 652·bytes`, the +same single source of truth the packer uses); regenerate with +`python3 ~/benchdata/prof/over_cap.py [ram_gb]`. + +--- + +## Data + +### Init shards (12) — variables summed over member constants + +| shard | blocks | hb | bytes | subst | cycles | +|---|---:|---:|---:|---:|---:| +| 803 | 3 | 3,117 | 23,174 | 6,785 | 771,527,739 | +| 806 | 6 | 4,586 | 32,062 | 10,479 | 810,732,054 | +| 502 | 2 | 4,057 | 24,927 | 15,452 | 1,034,769,720 | +| 68 | 37 | 2,806 | 115,271 | 67,589 | 1,127,754,269 | +| 957 | 278 | 2,672 | 397,663 | 145,185 | 1,382,497,091 | +| 290 | 19 | 4,540 | 72,883 | 75,048 | 1,397,057,638 | +| 383 | 87 | 3,370 | 162,554 | 170,516 | 1,584,528,514 | +| 870 | 311 | 4,151 | 255,573 | 173,497 | 1,773,123,833 | +| 273 | 101 | 4,911 | 159,997 | 227,820 | 1,782,171,986 | +| 815 | 428 | 3,823 | 378,779 | 253,556 | 2,526,432,086 | +| 634 | 1 | 32,843 | 2,308 | 513 | 5,695,914,962 | +| 630 | 1 | 32,843 | 2,309 | 513 | 5,697,160,198 | + +Shards 630/634 are one atomic constant each (`Int*.instRxcHasSize_eq`): tiny +`bytes`/`subst` but huge cycles, driven by `hb` (deep nat-range def-eq) — the +"expensive atomic" case (Finding 4). + +### Full-closure single constants — `--constant`, diverse shapes + +One library constant each, checked full-closure (the constant and its whole +dependency closure). The 35 Init constants below (over `initstd.ixe`) are shown +in full; the cross-library fit adds **19 more Init/Std** (incl. `Std.Time.*`, +`ByteSlice`) and **10 Mathlib** (`Finset.*`, `Polynomial.eval`, `Multiset.sort`, +`Nat.fib`, …, over `compilemathlib.ixe`), plus 12 small programs — 76 points +total, all measured (`~/benchdata/prof/{onlyconst,new,mathlib,smallenv}_*.tsv`). +All ran with zero failures. + +| constant | shape | hb | block_bytes | subst | cycles | +|---|---|--:|--:|--:|--:| +| `Nat` | inductive type | 3 | 113 | 0 | 975,244 | +| `Eq.rec` | recursor | 5 | 206 | 197 | 2,348,520 | +| `Array.toList` | array→list | 20 | 281 | 160 | 2,580,844 | +| `Acc.rec` | accessibility recursor | 10 | 315 | 770 | 5,105,888 | +| `Sum.elim` | sum eliminator | 15 | 693 | 745 | 6,618,130 | +| `Option.bind` | monadic bind | 30 | 945 | 1,052 | 7,440,608 | +| `Prod.map` | product map | 19 | 804 | 1,170 | 8,177,456 | +| `Nat.add` | function reduction | 61 | 1,889 | 1,074 | 10,606,339 | +| `WellFounded.fix` | well-founded fixpoint | 34 | 1,108 | 2,199 | 13,415,585 | +| `List.range` | list generator | 83 | 2,727 | 1,316 | 13,666,491 | +| `List.foldr` | list fold | 79 | 2,383 | 2,026 | 16,707,100 | +| `Int.add` | integer addition | 170 | 6,034 | 3,290 | 27,635,032 | +| `BitVec.toFin` | bitvec→fin | 176 | 5,913 | 3,055 | 28,681,028 | +| `USize.toNat` | fixed-width→nat | 198 | 8,391 | 3,652 | 35,811,906 | +| `Nat.add_comm` | arithmetic theorem | 360 | 6,908 | 3,998 | 53,239,676 | +| `Nat.decEq` | decidable equality | 266 | 8,116 | 6,747 | 57,411,966 | +| `Nat.decLe` | decidable order | 709 | 21,222 | 18,105 | 143,391,161 | +| `Nat.strongRecOn` | strong recursion | 764 | 24,974 | 28,682 | 190,849,758 | +| `Int.emod` | integer mod | 1,405 | 42,899 | 34,415 | 269,380,418 | +| `Array.foldl` | array fold | 1,284 | 41,477 | 38,282 | 278,537,034 | +| `Nat.sub_le_of_le_add` | order theorem | 1,915 | 54,845 | 48,701 | 373,184,538 | +| `Int.gcd` | integer gcd | 2,158 | 65,618 | 52,973 | 409,112,011 | +| `Array.map` | array map | 2,415 | 69,838 | 54,424 | 443,199,245 | +| `Lean.Name.hash` | name hashing | 2,451 | 82,181 | 57,157 | 447,441,591 | +| `Nat.repr` | number→string | 2,658 | 91,470 | 62,863 | 498,729,913 | +| `BitVec.umod` | bitvec modulo | 2,585 | 89,060 | 71,781 | 526,467,117 | +| `String.intercalate` | string join | 3,004 | 99,770 | 73,875 | 599,428,829 | +| `Char.toLower` | char case map | 3,588 | 115,675 | 82,453 | 665,920,824 | +| `Vector.append` | vector append lemma | 6,260 | 205,557 | 231,194 | 1,614,275,115 | +| `Int.emod_emod_of_dvd` | omega theorem | 11,951 | 345,084 | 226,571 | 2,201,588,182 | +| `Fin.foldl` | finite-range fold | 24,695 | 778,754 | 536,883 | 5,110,854,190 | +| `List.mergeSort` | merge sort | 32,915 | 912,942 | 642,232 | 6,706,906,294 | +| `Array.binSearch` | binary search | 34,358 | 891,352 | 646,852 | 6,785,827,470 | +| `Array.qsort` | quicksort | 41,060 | 1,001,670 | 739,383 | 7,199,288,749 | +| `String.split` | string splitting | 39,939 | 1,141,622 | 928,723 | 8,657,387,499 | + +`Vector.extract_append` is omitted: its full-closure check exceeds the executor +(the un-shardable case); subject-only it is 74,135,524 cycles. + +--- + +## Reproduce + +```bash +# profile out of circuit (per-block hb/bytes/subst + delta graph): env.ixe → +# env.ixprof, and print the kernel-work breakdown + predicted Zisk leaf cost/RAM +lake exe ix profile env.ixe + +# pack the .ixprof into shards under a RAM/cycle cap (offline, no kernel re-run) +lake exe ix shard env.ixprof --max-ram 256 # or --max-cycles C / --shards N + +# measure guest cycles: a shard/env, or one named constant +cargo run --release --bin zisk-host -- --execute --ixe \ + [--shard-plan --only-shard K] +cargo run --release --bin zisk-host -- --execute --ixe initstd.ixe \ + --constant "Nat.add_comm" [--skip-deps] # full-closure / subject-only + +# fits +python3 ~/benchdata/prof/fit_xlib.py # full-closure model (76 pts, Init+Std+Mathlib) +python3 ~/benchdata/prof/fit_onlyconst.py # subject-only model +python3 ~/benchdata/prof/compare.py # members-only vs full-closure costing +``` + +The cost model is the single source of truth in `crates/kernel/src/shard.rs` +(`block_step_cost`, `partition_for_cycle_cap`, `cycle_cap_for_ram`), consumed by +both the `ix profile` breakdown and the `ix shard` packer. Counters in +`profile.rs` (`bump_subst_nodes`/`bump_whnf`/`bump_def_eq`) record on every +native profiling run and compile out on the zkvm target. + +--- + +## Caveats + +- The full-closure model is n=76 across Init/Std/Mathlib (per-library MAPE + 5–8%); the shard model is still n=12 and Init-derived, so the ~190M shard + intercept is the term most likely to + shift on Std/Mathlib — worth re-checking there. +- RAM/prove-time models are for one GPU at `--max-witness-stored 5`; both scale + with that setting. +- Cycle counts are deterministic for a fixed guest ELF; the profiler counters + compile out on the zkvm target, so the proven ELF is unaffected. +- **Regimes have distinct coefficients and don't transfer.** + `instRxcHasSize_eq` (a single-block atomic shard) is predicted at **1.00×** by + the *shard* model (167,900·hb); applying the *full-closure* model to it + underpredicts ~2× — a regime mismatch, not a model failure. +- **Edge: near-pure-substitution constants.** Auto-generated `*.mk.injEq` / `.inj` + lemmas (huge `subst`, ~zero `hb`/`nat`) are *over*predicted ~1.3–1.6× by the + full-closure model — pure substitution is cheaper per node than the + mixed-calibration `subst` rate. Including such exemplars in the calibration + tightens them to ≤1.3× (safe, over-provision direction). The within-set worst + residual is 1.27×. +- **`nat_arith` does not help.** It is near-zero across all constants (the bignum + binop path is rarely exercised; max ≈1,600 vs `hb`/`subst` in the 10⁴–10⁵ + range), and every outlier is substitution-bound, not arithmetic-bound — so + reduction counters (`hb`, `subst`), which are 0.98–1.0 collinear on real code, + remain the right and sufficient variables. diff --git a/flake.lock b/flake.lock index 74d923cef..7468797d5 100644 --- a/flake.lock +++ b/flake.lock @@ -76,6 +76,21 @@ "type": "github" } }, + "crane_3": { + "locked": { + "lastModified": 1774313767, + "narHash": "sha256-hy0XTQND6avzGEUFrJtYBBpFa/POiiaGBr2vpU6Y9tY=", + "owner": "ipetkov", + "repo": "crane", + "rev": "3d9df76e29656c679c744968b17fbaf28f0e923d", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, "fenix": { "inputs": { "nixpkgs": [ @@ -121,6 +136,28 @@ "type": "github" } }, + "fenix_3": { + "inputs": { + "nixpkgs": [ + "zisk", + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src_3" + }, + "locked": { + "lastModified": 1774596377, + "narHash": "sha256-DiSLMxyTwIUAlhOe34r6kKNQRv6PTF+vf0MG45mAyn4=", + "owner": "nix-community", + "repo": "fenix", + "rev": "a88a1c8cf2f094da6347fcec54089f4bcb518409", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, "flake-parts": { "inputs": { "nixpkgs-lib": "nixpkgs-lib" @@ -175,6 +212,42 @@ "type": "github" } }, + "flake-parts_4": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib_4" + }, + "locked": { + "lastModified": 1772408722, + "narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_5": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib_5" + }, + "locked": { + "lastModified": 1772408722, + "narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, "lean4-nix": { "inputs": { "flake-parts": "flake-parts_3", @@ -255,6 +328,52 @@ "type": "github" } }, + "nixpkgs-lib_4": { + "locked": { + "lastModified": 1772328832, + "narHash": "sha256-e+/T/pmEkLP6BHhYjx6GmwP5ivonQQn0bJdH9YrRB+Q=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "c185c7a5e5dd8f9add5b2f8ebeff00888b070742", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "nixpkgs-lib_5": { + "locked": { + "lastModified": 1772328832, + "narHash": "sha256-e+/T/pmEkLP6BHhYjx6GmwP5ivonQQn0bJdH9YrRB+Q=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "c185c7a5e5dd8f9add5b2f8ebeff00888b070742", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1774386573, + "narHash": "sha256-4hAV26quOxdC6iyG7kYaZcM3VOskcPUrdCQd/nx8obc=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "46db2e09e1d3f113a13c0d7b81e2f221c63b8ce9", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "root": { "inputs": { "blake3-lean": "blake3-lean", @@ -265,7 +384,9 @@ "nixpkgs": [ "lean4-nix", "nixpkgs" - ] + ], + "sp1": "sp1", + "zisk": "zisk" } }, "rust-analyzer-src": { @@ -301,6 +422,66 @@ "repo": "rust-analyzer", "type": "github" } + }, + "rust-analyzer-src_3": { + "flake": false, + "locked": { + "lastModified": 1774569884, + "narHash": "sha256-E8iWEPzg7OnE0XXXjo75CX7xFauqzJuGZ5wSO9KS8Ek=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "443ddcddd0c73b07b799d052f5ef3b448c2f3508", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + }, + "sp1": { + "inputs": { + "flake-parts": "flake-parts_4", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1778884899, + "narHash": "sha256-kQahZHfD2f6tEaqD6WBDHDnjSr5m8x/IIS8VKu5glTw=", + "owner": "argumentcomputer", + "repo": "sp1.nix", + "rev": "048405ea52a9acb3fdd4eb7ad8c82346e93c4924", + "type": "github" + }, + "original": { + "owner": "argumentcomputer", + "repo": "sp1.nix", + "type": "github" + } + }, + "zisk": { + "inputs": { + "crane": "crane_3", + "fenix": "fenix_3", + "flake-parts": "flake-parts_5", + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1778885240, + "narHash": "sha256-hjbDg9V2q5fwoeBBjE/14a3YwQGwMImV/Apu0CMYSVY=", + "owner": "argumentcomputer", + "repo": "zisk.nix", + "rev": "d7fd3b8353dada23f9530716d1d8265b10c7808e", + "type": "github" + }, + "original": { + "owner": "argumentcomputer", + "ref": "fixups", + "repo": "zisk.nix", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index ad7c254b9..3188ce4c8 100644 --- a/flake.nix +++ b/flake.nix @@ -35,18 +35,30 @@ # System packages, follows lean4-nix so we stay in sync inputs.lean4-nix.follows = "lean4-nix"; }; + + # Zisk dev shell (cargo-zisk, ziskemu, RISC-V toolchain) for `zisk-guest`. + zisk.url = "github:argumentcomputer/zisk.nix/fixups"; + + # SP1 dev shell (cargo-prove + succinct Rust toolchain) for `sp1/guest`. + sp1 = { + url = "github:argumentcomputer/sp1.nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; - outputs = inputs @ { - nixpkgs, - flake-parts, - lean4-nix, - fenix, - crane, - blake3-lean, - ... - }: - flake-parts.lib.mkFlake {inherit inputs;} { + outputs = + inputs@{ + nixpkgs, + flake-parts, + lean4-nix, + fenix, + crane, + blake3-lean, + zisk, + sp1, + ... + }: + flake-parts.lib.mkFlake { inherit inputs; } { # Systems we want to build for systems = [ "aarch64-darwin" @@ -55,149 +67,197 @@ "x86_64-linux" ]; - perSystem = { - system, - pkgs, - ... - }: let - # Pins the Rust toolchain - rustToolchain = fenix.packages.${system}.fromToolchainFile { - file = ./rust-toolchain.toml; - sha256 = "sha256-sqSWJDUxc+zaz1nBWMAJKTAGBuGWP25GCftIOlCEAtA="; - }; + perSystem = + { + system, + pkgs, + ... + }: + let + # Pins the Rust toolchain + rustToolchain = fenix.packages.${system}.fromToolchainFile { + file = ./rust-toolchain.toml; + sha256 = "sha256-sqSWJDUxc+zaz1nBWMAJKTAGBuGWP25GCftIOlCEAtA="; + }; + + # Rust package + craneLib = (crane.mkLib pkgs).overrideToolchain rustToolchain; + src = craneLib.cleanCargoSource ./.; + craneArgs = { + inherit src; + pname = "ix"; + version = "0.1.0"; + strictDeps = true; - # Rust package - craneLib = (crane.mkLib pkgs).overrideToolchain rustToolchain; - src = craneLib.cleanCargoSource ./.; - craneArgs = { - inherit src; - strictDeps = true; - - # build.rs uses LEAN_SYSROOT to locate lean/lean.h for bindgen - LEAN_SYSROOT = "${pkgs.lean.lean-all}"; - # bindgen needs libclang to parse C headers - LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; - - buildInputs = - [] - ++ pkgs.lib.optionals pkgs.stdenv.isDarwin [ - # Additional darwin specific inputs can be set here - pkgs.libiconv + # build.rs uses LEAN_SYSROOT to locate lean/lean.h for bindgen + LEAN_SYSROOT = "${pkgs.lean.lean-all}"; + # bindgen needs libclang to parse C headers + LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; + + buildInputs = + [ ] + ++ pkgs.lib.optionals pkgs.stdenv.isDarwin [ + # Additional darwin specific inputs can be set here + pkgs.libiconv + ]; + }; + cargoArtifacts = craneLib.buildDepsOnly craneArgs; + + # Test build: parallel + test-ffi (only used by ixTest) + rustPkg = craneLib.buildPackage ( + craneArgs + // { + inherit cargoArtifacts; + cargoExtraArgs = "--locked --features parallel,test-ffi"; + } + ); + + # Release build without test-ffi (for distribution) + rustPkgRelease = craneLib.buildPackage ( + craneArgs + // { + inherit cargoArtifacts; + cargoExtraArgs = "--locked --features parallel"; + } + ); + + # Lake package + lake2nix = pkgs.callPackage lean4-nix.lake { }; + lakeDeps = lake2nix.buildDeps { + src = ./.; + depOverrideDeriv = { + Blake3 = blake3-lean.packages.${system}.rust; + }; + }; + # Shared Lake build args: patches out the Cargo build (Crane handles it) + mkLakeBuildArgs = rustLib: { + inherit lakeDeps; + src = ./.; + # Don't build the `ix_rs` static lib with Lake, since we build it with Crane + postPatch = '' + substituteInPlace lakefile.lean --replace-fail 'proc { cmd := "cargo"' '--proc { cmd := "cargo"' + ''; + # Symlink the Crane-built static lib to where Lake expects it + postConfigure = '' + mkdir -p target/release + ln -s ${rustLib}/lib/libix_ffi.a target/release/ + ''; + buildInputs = [ + pkgs.gmp + pkgs.lean.lean-all + pkgs.rsync ]; - }; - cargoArtifacts = craneLib.buildDepsOnly craneArgs; - - # Test build: parallel + test-ffi (only used by ixTest) - rustPkg = craneLib.buildPackage (craneArgs - // { - inherit cargoArtifacts; - cargoExtraArgs = "--locked --features parallel,test-ffi"; - }); - - # Release build without test-ffi (for distribution) - rustPkgRelease = craneLib.buildPackage (craneArgs - // { - inherit cargoArtifacts; - cargoExtraArgs = "--locked --features parallel"; - }); - - # Lake package - lake2nix = pkgs.callPackage lean4-nix.lake {}; - lakeDeps = lake2nix.buildDeps { - src = ./.; - depOverrideDeriv = { - Blake3 = blake3-lean.packages.${system}.rust; }; - }; - # Shared Lake build args: patches out the Cargo build (Crane handles it) - mkLakeBuildArgs = rustLib: { - inherit lakeDeps; - src = ./.; - # Don't build the `ix_rs` static lib with Lake, since we build it with Crane - postPatch = '' - substituteInPlace lakefile.lean --replace-fail 'proc { cmd := "cargo"' '--proc { cmd := "cargo"' - ''; - # Symlink the Crane-built static lib to where Lake expects it - postConfigure = '' - mkdir -p target/release - ln -s ${rustLib}/lib/libix_rs.a target/release/ - ''; - buildInputs = [pkgs.gmp pkgs.lean.lean-all pkgs.rsync]; - }; - # Release build args (no test-ffi symbols) - lakeBuildArgs = mkLakeBuildArgs rustPkgRelease; - # Test build args (includes test-ffi symbols) - lakeTestBuildArgs = mkLakeBuildArgs rustPkg; - - ixLib = lake2nix.mkPackage (lakeBuildArgs - // { - name = "Ix"; - buildLibrary = true; - }); - lakeBinArgs = - lakeBuildArgs - // { + # Release build args (no test-ffi symbols) + lakeBuildArgs = mkLakeBuildArgs rustPkgRelease; + # Test build args (includes test-ffi symbols) + lakeTestBuildArgs = mkLakeBuildArgs rustPkg; + + ixLib = lake2nix.mkPackage ( + lakeBuildArgs + // { + name = "Ix"; + buildLibrary = true; + } + ); + lakeBinArgs = lakeBuildArgs // { lakeArtifacts = ixLib; # Binaries that import Ix.Meta need .olean files at runtime via LEAN_PATH installArtifacts = true; }; - leanPath = pkgs.lib.concatStringsSep ":" ( - map (d: "${d}/.lake/build/lib/lean") ([ixLib] ++ builtins.attrValues lakeDeps) - ); - wrapBin = drv: - pkgs.runCommand drv.name {nativeBuildInputs = [pkgs.makeWrapper];} '' - mkdir -p $out/bin - for f in ${drv}/bin/*; do - [ -x "$f" ] || continue - makeWrapper "$f" "$out/bin/$(basename "$f")" \ - --set LEAN_SYSROOT "${pkgs.lean.lean-all}" \ - --set LEAN_PATH "${drv}/.lake/build/lib/lean:${leanPath}" - done - ''; - ixCLI = wrapBin (lake2nix.mkPackage (lakeBinArgs // {name = "ix";})); - # Test binary links rustPkg (with test-ffi) instead of rustPkgRelease - ixTest = wrapBin (lake2nix.mkPackage (lakeTestBuildArgs - // { - name = "IxTests"; - installArtifacts = true; - })); - ZKVotingProver = wrapBin (lake2nix.mkPackage (lakeBinArgs - // { - name = "Apps.ZKVoting.Prover"; - installArtifacts = true; - })); - in { - # Lean overlay - _module.args.pkgs = import nixpkgs { - inherit system; - overlays = [(lean4-nix.readToolchainFile ./lean-toolchain)]; - }; + leanPath = pkgs.lib.concatStringsSep ":" ( + map (d: "${d}/.lake/build/lib/lean") ([ ixLib ] ++ builtins.attrValues lakeDeps) + ); + wrapBin = + drv: + pkgs.runCommand drv.name { nativeBuildInputs = [ pkgs.makeWrapper ]; } '' + mkdir -p $out/bin + for f in ${drv}/bin/*; do + [ -x "$f" ] || continue + makeWrapper "$f" "$out/bin/$(basename "$f")" \ + --set LEAN_SYSROOT "${pkgs.lean.lean-all}" \ + --set LEAN_PATH "${drv}/.lake/build/lib/lean:${leanPath}" + done + ''; + ixCLI = wrapBin (lake2nix.mkPackage (lakeBinArgs // { name = "ix"; })); + # Test binary links rustPkg (with test-ffi) instead of rustPkgRelease + ixTest = wrapBin ( + lake2nix.mkPackage ( + lakeTestBuildArgs + // { + name = "IxTests"; + installArtifacts = true; + } + ) + ); + ZKVotingProver = wrapBin ( + lake2nix.mkPackage ( + lakeBinArgs + // { + name = "Apps.ZKVoting.Prover"; + installArtifacts = true; + } + ) + ); + in + { + # Lean overlay + _module.args.pkgs = import nixpkgs { + inherit system; + overlays = [ (lean4-nix.readToolchainFile ./lean-toolchain) ]; + }; - packages = { - default = ixLib; - ix = ixCLI; - test = ixTest; - zkv-prover = ZKVotingProver // {meta.mainProgram = "Apps-ZKVoting-Prover";}; - }; + packages = { + default = ixLib; + ix = ixCLI; + test = ixTest; + zkv-prover = ZKVotingProver // { + meta.mainProgram = "Apps-ZKVoting-Prover"; + }; + }; - # Provide a unified dev shell with Lean + Rust - devShells.default = pkgs.mkShell { - # Add libclang for FFI with rust-bindgen - LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; - packages = with pkgs; [ - pkg-config - openssl - clang - rustToolchain - rust-analyzer - lean.lean-all # Includes Lean compiler, lake, stdlib, etc. - cargo-deny - valgrind - ]; - }; + # Lean + Rust shell for host development (`cargo build`, `lake build`). + devShells.default = pkgs.mkShell { + LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; + packages = with pkgs; [ + pkg-config + openssl + clang + rustToolchain + rust-analyzer + lean.lean-all + cargo-deny + valgrind + ]; + }; - formatter = pkgs.alejandra; - }; + # Zisk shell for `zisk-guest/` (cargo-zisk, ziskemu, RISC-V toolchain). + # Kept separate from `default`: merging cross-pollinates NIX_CFLAGS_COMPILE + # between zisk.nix's and this flake's nixpkgs, which breaks bindgen on + # `lean.h`. + devShells.zisk = zisk.devShells.${system}.default; + + # SP1 shell for `sp1/host` + `sp1/guest`: host Rust toolchain plus + # cargo-prove and the succinct Rust toolchain (~/.sp1) from sp1.nix. + # `rustup-shim` wraps the host `rustc` to dispatch to the succinct + # toolchain when `RUSTUP_TOOLCHAIN=succinct` (set by `sp1-build`); the + # plain host rustc doesn't know `riscv64im-succinct-zkvm-elf`. + # `sp1-prover-types`'s build script needs `protoc`. + devShells.sp1 = pkgs.mkShell { + name = "sp1"; + inputsFrom = [ sp1.devShells.${system}.default ]; + LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; + packages = with pkgs; [ + pkg-config + openssl + protobuf + clang + (sp1.packages.${system}.rustup-shim.override { inherit rustToolchain; }) + ]; + }; + + formatter = pkgs.alejandra; + }; }; } diff --git a/lake-manifest.json b/lake-manifest.json index 05e505ee6..2c6d163ca 100644 --- a/lake-manifest.json +++ b/lake-manifest.json @@ -25,10 +25,10 @@ "type": "git", "subDir": null, "scope": "", - "rev": "aaf530784082a2c00b0a93648741429d274102ca", + "rev": "d15f36cf76eb5834b0e623e02b97fd4d95e56cc7", "name": "Blake3", "manifestFile": "lake-manifest.json", - "inputRev": "aaf530784082a2c00b0a93648741429d274102ca", + "inputRev": "d15f36cf76eb5834b0e623e02b97fd4d95e56cc7", "inherited": false, "configFile": "lakefile.lean"}, {"url": "https://github.com/argumentcomputer/LSpec", diff --git a/lakefile.lean b/lakefile.lean index b192de937..2b99b20ce 100644 --- a/lakefile.lean +++ b/lakefile.lean @@ -8,7 +8,7 @@ require LSpec from git "https://github.com/argumentcomputer/LSpec" @ "d3c15b93a1dd4e7c8d5c0c3825c9555737e55c3e" require Blake3 from git - "https://github.com/argumentcomputer/Blake3.lean" @ "aaf530784082a2c00b0a93648741429d274102ca" + "https://github.com/argumentcomputer/Blake3.lean" @ "d15f36cf76eb5834b0e623e02b97fd4d95e56cc7" require Cli from git "https://github.com/leanprover/lean4-cli" @ "v4.29.0" @@ -40,7 +40,7 @@ def cargoArgs (testFfi : Bool := false) (net : Bool := false) : IO (Array String if ixNoPar != some "1" then features := features.push "parallel" if net && !System.Platform.isOSX then features := features.push "net" if testFfi then features := features.push "test-ffi" - let buildArgs := #["build", "--release"] + let buildArgs := #["build", "--release", "-p", "ix-ffi"] if features.isEmpty then return buildArgs else return buildArgs ++ #["--features", ",".intercalate features.toList] @@ -48,7 +48,7 @@ def cargoArgs (testFfi : Bool := false) (net : Bool := false) : IO (Array String target ix_rs pkg : FilePath := do let args ← cargoArgs proc { cmd := "cargo", args, cwd := pkg.dir } (quiet := true) - inputBinFile $ pkg.dir / "target" / "release" / nameToStaticLib "ix_rs" + inputBinFile $ pkg.dir / "target" / "release" / nameToStaticLib "ix_ffi" /-- Rebuild the Rust static lib with `test-ffi`. Only triggered by `lake test` (via `moreLinkObjs` on `IxTests`). @@ -57,7 +57,7 @@ target ix_rs_test pkg : FilePath := do let _ ← ix_rs.fetch let args ← cargoArgs (testFfi := true) proc { cmd := "cargo", args, cwd := pkg.dir } (quiet := true) - inputBinFile $ pkg.dir / "target" / "release" / nameToStaticLib "ix_rs" + inputBinFile $ pkg.dir / "target" / "release" / nameToStaticLib "ix_ffi" /-- Build the Rust static lib with `net` for the `ix` CLI. Fetches `ix_rs` first to guarantee ordering before overwriting the lib. -/ @@ -65,7 +65,7 @@ target ix_rs_net pkg : FilePath := do let _ ← ix_rs.fetch let args ← cargoArgs (net := true) proc { cmd := "cargo", args, cwd := pkg.dir } (quiet := true) - inputBinFile $ pkg.dir / "target" / "release" / nameToStaticLib "ix_rs" + inputBinFile $ pkg.dir / "target" / "release" / nameToStaticLib "ix_ffi" end FFI diff --git a/sp1/.cargo/config.toml b/sp1/.cargo/config.toml new file mode 100644 index 000000000..9818452d8 --- /dev/null +++ b/sp1/.cargo/config.toml @@ -0,0 +1,9 @@ +[build] +rustflags = ["-C", "target-cpu=native"] + +# Wraps every binary with scripts/cuda-runner.sh, which cleans orphaned +# sp1-gpu-server sockets and retries on the SP1 SDK's CUDA-init race. +# Pass-through (no overhead, no retries) when SP1_PROVER != "cuda". +# Path is relative to cwd; this expects cargo to be invoked from sp1/. +[target.x86_64-unknown-linux-gnu] +runner = "./scripts/cuda-runner.sh" diff --git a/sp1/Cargo.lock b/sp1/Cargo.lock new file mode 100644 index 000000000..a5f9903d6 --- /dev/null +++ b/sp1/Cargo.lock @@ -0,0 +1,5920 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addchain" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e33f6a175ec6a9e0aca777567f9ff7c3deefc255660df887e7fa3585e9801d8" +dependencies = [ + "num-bigint 0.3.3", + "num-integer", + "num-traits", +] + +[[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "async-scoped" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4042078ea593edffc452eef14e99fdb2b120caa4ad9618bcdeabc4a023b98740" +dependencies = [ + "futures", + "pin-project", + "tokio", +] + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "atomic" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "axum" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper", + "tower 0.5.3", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", +] + +[[package]] +name = "backtrace" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "serde", + "windows-link", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bignat" +version = "0.1.0" +source = "git+https://github.com/argumentcomputer/lean-ffi.git?rev=839b405de708b80504fda39abe1402114b4135a5#839b405de708b80504fda39abe1402114b4135a5" +dependencies = [ + "num-bigint 0.4.6", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake3" +version = "1.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "cpufeatures 0.3.0", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array 0.14.9", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "byte-slice-cast" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "camino" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" +dependencies = [ + "serde_core", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "cc" +version = "1.2.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "iana-time-zone", + "num-traits", + "windows-link", +] + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "console" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width", + "windows-sys 0.59.0", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const_format" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4481a617ad9a412be3b97c5d403fef8ed023103368908b9c50af598ff467cc1e" +dependencies = [ + "const_format_proc_macros", + "konst", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "crash-context" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031ed29858d90cfdf27fe49fae28028a1f20466db97962fa2f4ea34809aeebf3" +dependencies = [ + "cfg-if", + "libc", + "mach2", +] + +[[package]] +name = "crash-handler" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0df5c9639f4942eb7702b964b3f9adf03a55724a57558cc177407388a8b936e2" +dependencies = [ + "cfg-if", + "crash-context", + "libc", + "mach2", + "parking_lot", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array 0.14.9", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array 0.14.9", + "typenum", +] + +[[package]] +name = "dashmap" +version = "6.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6361d5c062261c78a176addb82d4c821ae42bed6089de0e12603cd25de2059c" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", + "rayon", +] + +[[package]] +name = "dashu" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b3e5ac1e23ff1995ef05b912e2b012a8784506987a2651552db2c73fb3d7e0" +dependencies = [ + "dashu-base", + "dashu-float", + "dashu-int", + "dashu-macros", + "dashu-ratio", + "rustversion", +] + +[[package]] +name = "dashu-base" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b80bf6b85aa68c58ffea2ddb040109943049ce3fbdf4385d0380aef08ef289" + +[[package]] +name = "dashu-float" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85078445a8dbd2e1bd21f04a816f352db8d333643f0c9b78ca7c3d1df71063e7" +dependencies = [ + "dashu-base", + "dashu-int", + "num-modular", + "num-order", + "rustversion", + "static_assertions", +] + +[[package]] +name = "dashu-int" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee99d08031ca34a4d044efbbb21dff9b8c54bb9d8c82a189187c0651ffdb9fbf" +dependencies = [ + "cfg-if", + "dashu-base", + "num-modular", + "num-order", + "rustversion", + "static_assertions", +] + +[[package]] +name = "dashu-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93381c3ef6366766f6e9ed9cf09e4ef9dec69499baf04f0c60e70d653cf0ab10" +dependencies = [ + "dashu-base", + "dashu-float", + "dashu-int", + "dashu-ratio", + "paste", + "proc-macro2", + "quote", + "rustversion", +] + +[[package]] +name = "dashu-ratio" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e33b04dd7ce1ccf8a02a69d3419e354f2bbfdf4eb911a0b7465487248764c9" +dependencies = [ + "dashu-base", + "dashu-float", + "dashu-int", + "num-modular", + "num-order", + "rustversion", +] + +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "uuid", +] + +[[package]] +name = "deepsize2" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b5184084af9beed35eecbf4c36baf6e26b9dc47b61b74e02f930c72a58e71b" +dependencies = [ + "deepsize_derive2", + "hashbrown 0.14.5", +] + +[[package]] +name = "deepsize_derive2" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0f8817865cacf3b93b943ca06b0fc5fd8e99eabfdb7ea5d296efcbc4afc4f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive-where" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dynasm" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7d4c414c94bc830797115b8e5f434d58e7e80cb42ba88508c14bc6ea270625" +dependencies = [ + "bitflags", + "byteorder", + "lazy_static", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "dynasmrt" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "602f7458a3859195fb840e6e0cce5f4330dd9dfbfece0edaf31fe427af346f55" +dependencies = [ + "byteorder", + "dynasm", + "fnv", + "memmap2", +] + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "serdect", + "signature", + "spki", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +dependencies = [ + "serde", +] + +[[package]] +name = "elf" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array 0.14.9", + "group", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "serdect", + "subtle", + "zeroize", +] + +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + +[[package]] +name = "enum-map" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" +dependencies = [ + "enum-map-derive", + "serde", +] + +[[package]] +name = "enum-map-derive" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "eventsource-stream" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab" +dependencies = [ + "futures-core", + "nom", + "pin-project-lite", +] + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "bitvec", + "byteorder", + "ff_derive", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "ff_derive" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f10d12652036b0e99197587c6ba87a8fc3031986499973c030d8b44fcc151b60" +dependencies = [ + "addchain", + "num-bigint 0.3.3", + "num-integer", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "gcd" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" + +[[package]] +name = "gecko_profile" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "890852c7e1e02bc6758e325d6b1e0236e4fbf21b492f585ce4d4715be54b4c6a" +dependencies = [ + "debugid", + "serde", + "serde_json", +] + +[[package]] +name = "gen_ops" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "304de19db7028420975a296ab0fcbbc8e69438c4ed254a1e41e2a7f37d5f0e0a" + +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "generic-array" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96512db27971c2c3eece70a1e106fbe6c87760234e31e8f7e5634912fe52794a" +dependencies = [ + "serde", + "typenum", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 5.3.0", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "h2" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap 2.14.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", + "serde", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "human-repr" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f58b778a5761513caf593693f8951c97a5b610841e754788400f32102eefdff1" + +[[package]] +name = "hyper" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2 0.6.3", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core 0.62.2", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "indenter" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "indicatif" +version = "0.17.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" +dependencies = [ + "console", + "number_prefix", + "portable-atomic", + "unicode-width", + "web-time", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "ix-common" +version = "0.1.0" +dependencies = [ + "bignat", + "blake3", + "indexmap 2.14.0", + "rustc-hash", +] + +[[package]] +name = "ix-kernel" +version = "0.1.0" +dependencies = [ + "bignat", + "blake3", + "dashmap", + "indexmap 2.14.0", + "itertools 0.14.0", + "ix-common", + "ixon", + "log", + "num-bigint 0.4.6", + "rayon", + "rustc-hash", +] + +[[package]] +name = "ixon" +version = "0.1.0" +dependencies = [ + "bignat", + "blake3", + "dashmap", + "indexmap 2.14.0", + "ix-common", + "memmap2", + "num-bigint 0.4.6", + "rayon", + "rustc-hash", + "sha2", + "tiny-keccak", +] + +[[package]] +name = "js-sys" +version = "0.3.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" +dependencies = [ + "cfg-if", + "futures-util", + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "serdect", + "sha2", + "signature", +] + +[[package]] +name = "konst" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128133ed7824fcd73d6e7b17957c5eb7bacb885649bd8c69708b2331a10bcefb" +dependencies = [ + "konst_macro_rules", +] + +[[package]] +name = "konst_macro_rules" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libredox" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +dependencies = [ + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5" + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.5", +] + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "mach2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" +dependencies = [ + "libc", +] + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "memfd" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad38eb12aea514a0466ea40a80fd8cc83637065948eb4a426e4aa46261175227" +dependencies = [ + "rustix", +] + +[[package]] +name = "memmap2" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +dependencies = [ + "libc", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "mti" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9563a7d5556636e74bbd8773241fbcbc5c89b9f6bfdc97b29b56e740c2c74b9" +dependencies = [ + "typeid_prefix", + "typeid_suffix", +] + +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "ntapi" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae" +dependencies = [ + "winapi", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint 0.4.6", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-modular" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17bb261bf36fa7d83f4c294f834e91256769097b3cb505d44831e0a179ac647f" + +[[package]] +name = "num-order" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537b596b97c40fcf8056d153049eb22f481c17ebce72a513ec9286e4986d1bb6" +dependencies = [ + "num-modular", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint 0.4.6", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "crc32fast", + "flate2", + "hashbrown 0.15.5", + "indexmap 2.14.0", + "memchr", + "ruzstd", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "opentelemetry" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b69a91d4893e713e06f724597ad630f1fa76057a5e1026c0ca67054a9032a76" +dependencies = [ + "futures-core", + "futures-sink", + "js-sys", + "once_cell", + "pin-project-lite", + "thiserror 1.0.69", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p3-air" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3a5de20a2301bf2530de1ceb13768ec3a80f729fbf8b72f813e30bc54c5bce2" +dependencies = [ + "p3-field", + "p3-matrix", + "serde", +] + +[[package]] +name = "p3-baby-bear" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d69e6e9af4eaaaa60f7bb9f0e0f73ebcbaefe7e00974d97ad0fa542d6a4f0890" +dependencies = [ + "cfg-if", + "num-bigint 0.4.6", + "p3-field", + "p3-mds", + "p3-poseidon2", + "p3-symmetric", + "rand 0.8.6", + "rustc_version", + "serde", +] + +[[package]] +name = "p3-bn254-fr" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2077757c7cb514202ccb5368f521f23f5709c720599e6545c683c66e0a52d2d8" +dependencies = [ + "ff", + "num-bigint 0.4.6", + "p3-field", + "p3-poseidon2", + "p3-symmetric", + "rand 0.8.6", + "serde", +] + +[[package]] +name = "p3-challenger" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6a908924d43e4cfb93fb41c8346cac211b70314385a9037e9241f5b7f3eaf77" +dependencies = [ + "p3-field", + "p3-maybe-rayon", + "p3-symmetric", + "p3-util", + "serde", + "tracing", +] + +[[package]] +name = "p3-commit" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50acacc7219fce6c01db938f82c1b21b5e7133990b7fff861f91534aeb569419" +dependencies = [ + "itertools 0.12.1", + "p3-challenger", + "p3-field", + "p3-matrix", + "p3-util", + "serde", +] + +[[package]] +name = "p3-dft" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be6408b10a2c27eb13a7d5580c546c2179a8dc7dbc10a990657311891f9b41c0" +dependencies = [ + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-util", + "tracing", +] + +[[package]] +name = "p3-field" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dc75969ca3ac847f43e632ab979d59ff7a68f9eac8dbf8edcbba47fc2e1d3aa" +dependencies = [ + "itertools 0.12.1", + "num-bigint 0.4.6", + "num-traits", + "p3-util", + "rand 0.8.6", + "serde", +] + +[[package]] +name = "p3-fri" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cbc4965ee488f3247867b7ec4bb005b8afa72cb0d461a4dcb1387ecab6426d5" +dependencies = [ + "itertools 0.12.1", + "p3-challenger", + "p3-commit", + "p3-dft", + "p3-field", + "p3-interpolation", + "p3-matrix", + "p3-maybe-rayon", + "p3-util", + "serde", + "tracing", +] + +[[package]] +name = "p3-interpolation" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08ad7e9f08c336d7ea39d12e11951188473542565323bac2a6535e536b58487d" +dependencies = [ + "p3-field", + "p3-matrix", + "p3-util", +] + +[[package]] +name = "p3-keccak-air" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f5bf177d56740078b5a5a842fa2393427796283dc8174b4a1a325c2d0b042de" +dependencies = [ + "p3-air", + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-util", + "tracing", +] + +[[package]] +name = "p3-koala-bear" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a9683cd0ef68100df7c62490533047bcf19c04c4a0fa1efc9d7c1e03e31f6b3" +dependencies = [ + "cfg-if", + "num-bigint 0.4.6", + "p3-field", + "p3-mds", + "p3-poseidon2", + "p3-symmetric", + "rand 0.8.6", + "rustc_version", + "serde", +] + +[[package]] +name = "p3-matrix" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75c3f150ceb90e09539413bf481e618d05ee19210b4e467d2902eb82d2e15281" +dependencies = [ + "itertools 0.12.1", + "p3-field", + "p3-maybe-rayon", + "p3-util", + "rand 0.8.6", + "serde", + "tracing", +] + +[[package]] +name = "p3-maybe-rayon" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0641952b42da45e1dfa2d4a2a3163e330f944ad9740942f35026c0a71a605f1" +dependencies = [ + "rayon", +] + +[[package]] +name = "p3-mds" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa4a5f250e174dcfca5cbeac6ad75713924e7e7320e0a335e3c50b8b1f4fe8ec" +dependencies = [ + "itertools 0.12.1", + "p3-dft", + "p3-field", + "p3-matrix", + "p3-symmetric", + "p3-util", + "rand 0.8.6", +] + +[[package]] +name = "p3-merkle-tree" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5703d9229d52a8c09970e4d722c3a8b4d37e688c306c3a1c03b872efcd204e6" +dependencies = [ + "itertools 0.12.1", + "p3-commit", + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-symmetric", + "p3-util", + "serde", + "tracing", +] + +[[package]] +name = "p3-poseidon2" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "522986377b2164c5f94f2dae88e0e0a3d169cc6239202ef4aeb4322d60feffd0" +dependencies = [ + "gcd", + "p3-field", + "p3-mds", + "p3-symmetric", + "rand 0.8.6", + "serde", +] + +[[package]] +name = "p3-symmetric" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9047ce85c086a9b3f118e10078f10636f7bfeed5da871a04da0b61400af8793a" +dependencies = [ + "itertools 0.12.1", + "p3-field", + "serde", +] + +[[package]] +name = "p3-uni-stark" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc3dfdeba14d8db621c4e52dd63973384ff35f353fd750154ff88397f4ea5adf" +dependencies = [ + "itertools 0.12.1", + "p3-air", + "p3-challenger", + "p3-commit", + "p3-dft", + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-util", + "serde", + "tracing", +] + +[[package]] +name = "p3-util" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff962f8eaa5f36e0447cee7c241f6b4b475fadf3ee61f154327a26bb4e009ba" +dependencies = [ + "serde", +] + +[[package]] +name = "parity-scale-codec" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" +dependencies = [ + "arrayvec", + "byte-slice-cast", + "const_format", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "rustversion", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap 2.14.0", +] + +[[package]] +name = "pin-project" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit 0.25.11+spec-1.1.0", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" +dependencies = [ + "heck", + "itertools 0.14.0", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn 2.0.117", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "prost-types" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +dependencies = [ + "prost", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2 0.6.3", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.4", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2 0.6.3", + "tracing", + "windows-sys 0.60.2", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "range-set-blaze" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8421b5d459262eabbe49048d362897ff3e3830b44eac6cfe341d6acb2f0f13d2" +dependencies = [ + "gen_ops", + "itertools 0.12.1", + "num-integer", + "num-traits", +] + +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "rayon-scan" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f87cc11a0140b4b0da0ffc889885760c61b13672d80a908920b2c0df078fa14" +dependencies = [ + "rayon", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tokio-util", + "tower 0.5.3", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "webpki-roots", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rrs-succinct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd079cd303257a4cb4e5aadfa79a7fe23f3c8301aa4740ccc3a99673485a352" +dependencies = [ + "downcast-rs", + "num_enum", + "paste", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ruzstd" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7c1c839d570d835527c9a5e4db7cb2198683a988cb9d7293fc8674e6bd58fc8" +dependencies = [ + "twox-hash", +] + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "scale-info" +version = "2.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346a3b32eba2640d17a9cb5927056b08f3de90f65b72fe09402c2ad07d684d0b" +dependencies = [ + "cfg-if", + "derive_more", + "parity-scale-codec", + "scale-info-derive", +] + +[[package]] +name = "scale-info-derive" +version = "2.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6630024bf739e2179b91fb424b28898baf819414262c5d376677dbff1fe7ebf" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "scc" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46e6f046b7fef48e2660c57ed794263155d713de679057f2d0c169bfc6e756cc" +dependencies = [ + "sdd", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sdd" +version = "3.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array 0.14.9", + "pkcs8", + "serdect", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_arrays" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94a16b99c5ea4fe3daccd14853ad260ec00ea043b2708d1fd1da3106dcd8d9df" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serdect" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" +dependencies = [ + "base16ct", + "serde", +] + +[[package]] +name = "serial_test" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "911bd979bf1070a3f3aa7b691a3b3e9968f339ceeec89e08c280a8a22207a32f" +dependencies = [ + "futures-executor", + "futures-util", + "log", + "once_cell", + "parking_lot", + "scc", + "serial_test_derive", +] + +[[package]] +name = "serial_test_derive" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a7d91949b85b0d2fb687445e448b40d322b6b3e4af6b44a29b21d9a5f33e6d9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "sha1_smol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "slop-air" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "p3-air", +] + +[[package]] +name = "slop-algebra" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "itertools 0.14.0", + "p3-field", + "serde", +] + +[[package]] +name = "slop-alloc" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "serde", + "slop-algebra", + "thiserror 1.0.69", +] + +[[package]] +name = "slop-baby-bear" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "lazy_static", + "p3-baby-bear", + "serde", + "slop-algebra", + "slop-challenger", + "slop-poseidon2", + "slop-symmetric", +] + +[[package]] +name = "slop-basefold" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "derive-where", + "itertools 0.14.0", + "serde", + "slop-algebra", + "slop-alloc", + "slop-baby-bear", + "slop-bn254", + "slop-challenger", + "slop-koala-bear", + "slop-merkle-tree", + "slop-multilinear", + "slop-primitives", + "slop-tensor", + "slop-utils", + "thiserror 1.0.69", +] + +[[package]] +name = "slop-basefold-prover" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "derive-where", + "itertools 0.14.0", + "rand 0.8.6", + "serde", + "slop-algebra", + "slop-alloc", + "slop-baby-bear", + "slop-basefold", + "slop-bn254", + "slop-challenger", + "slop-commit", + "slop-dft", + "slop-fri", + "slop-futures", + "slop-koala-bear", + "slop-merkle-tree", + "slop-multilinear", + "slop-tensor", + "thiserror 1.0.69", +] + +[[package]] +name = "slop-bn254" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "ff", + "p3-bn254-fr", + "serde", + "slop-algebra", + "slop-challenger", + "slop-poseidon2", + "slop-symmetric", +] + +[[package]] +name = "slop-challenger" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "futures", + "p3-challenger", + "serde", + "slop-algebra", + "slop-symmetric", +] + +[[package]] +name = "slop-commit" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "p3-commit", + "serde", + "slop-alloc", +] + +[[package]] +name = "slop-dft" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "p3-dft", + "serde", + "slop-algebra", + "slop-alloc", + "slop-matrix", + "slop-tensor", +] + +[[package]] +name = "slop-fri" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "p3-fri", +] + +[[package]] +name = "slop-futures" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "crossbeam", + "futures", + "pin-project", + "rayon", + "thiserror 1.0.69", + "tokio", + "tracing", +] + +[[package]] +name = "slop-jagged" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "derive-where", + "futures", + "itertools 0.14.0", + "num_cpus", + "rand 0.8.6", + "rayon", + "serde", + "slop-algebra", + "slop-alloc", + "slop-baby-bear", + "slop-basefold", + "slop-basefold-prover", + "slop-bn254", + "slop-challenger", + "slop-commit", + "slop-futures", + "slop-koala-bear", + "slop-merkle-tree", + "slop-multilinear", + "slop-stacked", + "slop-sumcheck", + "slop-symmetric", + "slop-tensor", + "slop-utils", + "thiserror 1.0.69", + "tracing", +] + +[[package]] +name = "slop-keccak-air" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "p3-keccak-air", +] + +[[package]] +name = "slop-koala-bear" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "lazy_static", + "p3-koala-bear", + "serde", + "slop-algebra", + "slop-challenger", + "slop-poseidon2", + "slop-symmetric", +] + +[[package]] +name = "slop-matrix" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "p3-matrix", +] + +[[package]] +name = "slop-maybe-rayon" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "p3-maybe-rayon", +] + +[[package]] +name = "slop-merkle-tree" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "derive-where", + "itertools 0.14.0", + "p3-merkle-tree", + "serde", + "slop-algebra", + "slop-alloc", + "slop-baby-bear", + "slop-bn254", + "slop-challenger", + "slop-commit", + "slop-futures", + "slop-koala-bear", + "slop-matrix", + "slop-poseidon2", + "slop-symmetric", + "slop-tensor", + "slop-utils", + "thiserror 1.0.69", +] + +[[package]] +name = "slop-multilinear" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "derive-where", + "futures", + "num_cpus", + "rand 0.8.6", + "rayon", + "serde", + "slop-algebra", + "slop-alloc", + "slop-challenger", + "slop-commit", + "slop-futures", + "slop-matrix", + "slop-tensor", +] + +[[package]] +name = "slop-poseidon2" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "p3-poseidon2", +] + +[[package]] +name = "slop-primitives" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "slop-algebra", +] + +[[package]] +name = "slop-stacked" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "derive-where", + "futures", + "itertools 0.14.0", + "serde", + "slop-algebra", + "slop-alloc", + "slop-basefold", + "slop-basefold-prover", + "slop-challenger", + "slop-commit", + "slop-futures", + "slop-merkle-tree", + "slop-multilinear", + "slop-tensor", + "thiserror 1.0.69", +] + +[[package]] +name = "slop-sumcheck" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "futures", + "itertools 0.14.0", + "rayon", + "serde", + "slop-algebra", + "slop-alloc", + "slop-baby-bear", + "slop-challenger", + "slop-multilinear", + "thiserror 1.0.69", +] + +[[package]] +name = "slop-symmetric" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "p3-symmetric", +] + +[[package]] +name = "slop-tensor" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "arrayvec", + "derive-where", + "itertools 0.14.0", + "rand 0.8.6", + "rayon", + "serde", + "slop-algebra", + "slop-alloc", + "slop-futures", + "slop-matrix", + "thiserror 1.0.69", + "transpose", +] + +[[package]] +name = "slop-uni-stark" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "p3-uni-stark", +] + +[[package]] +name = "slop-utils" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "p3-util", + "tracing-forest", + "tracing-subscriber", +] + +[[package]] +name = "slop-whir" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "derive-where", + "futures", + "itertools 0.14.0", + "rand 0.8.6", + "rayon", + "serde", + "slop-algebra", + "slop-alloc", + "slop-baby-bear", + "slop-basefold", + "slop-challenger", + "slop-commit", + "slop-dft", + "slop-jagged", + "slop-koala-bear", + "slop-matrix", + "slop-merkle-tree", + "slop-multilinear", + "slop-stacked", + "slop-tensor", + "slop-utils", + "thiserror 1.0.69", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "serde", +] + +[[package]] +name = "snowbridge-amcl" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460a9ed63cdf03c1b9847e8a12a5f5ba19c4efd5869e4a737e05be25d7c427e5" +dependencies = [ + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "sp1-build" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "anyhow", + "cargo_metadata", + "chrono", + "clap", + "dirs", + "sp1-primitives", +] + +[[package]] +name = "sp1-core-executor" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "bincode", + "bytemuck", + "cfg-if", + "clap", + "deepsize2", + "elf", + "enum-map", + "eyre", + "gecko_profile", + "hashbrown 0.14.5", + "hex", + "indicatif", + "itertools 0.14.0", + "memmap2", + "num", + "object", + "rrs-succinct", + "rustc-demangle", + "serde", + "serde_arrays", + "serde_json", + "slop-air", + "slop-algebra", + "slop-maybe-rayon", + "slop-symmetric", + "sp1-curves", + "sp1-hypercube", + "sp1-jit", + "sp1-primitives", + "strum", + "subenum", + "thiserror 1.0.69", + "tiny-keccak", + "tracing", + "typenum", + "vec_map", +] + +[[package]] +name = "sp1-core-executor-runner" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "base64", + "bincode", + "cargo_metadata", + "hashbrown 0.14.5", + "hex", + "libc", + "sha2", + "sp1-core-executor", + "sp1-core-executor-runner-binary", + "sp1-jit", + "sp1-primitives", + "sysinfo", + "tracing", + "uuid", +] + +[[package]] +name = "sp1-core-executor-runner-binary" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "bincode", + "crash-handler", + "libc", + "serde", + "sp1-core-executor", + "sp1-jit", + "tracing-subscriber", +] + +[[package]] +name = "sp1-core-machine" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "bincode", + "cfg-if", + "enum-map", + "futures", + "generic-array 1.1.0", + "hashbrown 0.14.5", + "itertools 0.14.0", + "num", + "num_cpus", + "rayon", + "rayon-scan", + "rrs-succinct", + "serde", + "serde_json", + "slop-air", + "slop-algebra", + "slop-challenger", + "slop-futures", + "slop-keccak-air", + "slop-matrix", + "slop-maybe-rayon", + "slop-uni-stark", + "snowbridge-amcl", + "sp1-core-executor", + "sp1-core-executor-runner", + "sp1-curves", + "sp1-derive", + "sp1-hypercube", + "sp1-jit", + "sp1-primitives", + "static_assertions", + "struct-reflection", + "strum", + "sysinfo", + "tempfile", + "thiserror 1.0.69", + "tokio", + "tracing", + "tracing-forest", + "tracing-subscriber", + "typenum", +] + +[[package]] +name = "sp1-cuda" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "bincode", + "bytes", + "reqwest", + "serde", + "serde_json", + "sp1-core-executor", + "sp1-core-machine", + "sp1-hypercube", + "sp1-primitives", + "sp1-prover", + "sp1-prover-types", + "thiserror 1.0.69", + "tokio", + "tracing", +] + +[[package]] +name = "sp1-curves" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "cfg-if", + "dashu", + "elliptic-curve", + "generic-array 1.1.0", + "itertools 0.14.0", + "k256", + "num", + "p256", + "serde", + "slop-algebra", + "snowbridge-amcl", + "sp1-primitives", + "typenum", +] + +[[package]] +name = "sp1-derive" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "sp1-host" +version = "0.1.0" +dependencies = [ + "anyhow", + "bincode", + "clap", + "human-repr", + "ix-common", + "ix-kernel", + "ixon", + "sp1-build", + "sp1-sdk", + "tokio", +] + +[[package]] +name = "sp1-hypercube" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "arrayref", + "deepsize2", + "derive-where", + "futures", + "hashbrown 0.14.5", + "itertools 0.14.0", + "num-bigint 0.4.6", + "num-traits", + "num_cpus", + "rayon", + "rayon-scan", + "serde", + "slop-air", + "slop-algebra", + "slop-alloc", + "slop-basefold", + "slop-basefold-prover", + "slop-bn254", + "slop-challenger", + "slop-commit", + "slop-futures", + "slop-jagged", + "slop-koala-bear", + "slop-matrix", + "slop-merkle-tree", + "slop-multilinear", + "slop-poseidon2", + "slop-stacked", + "slop-sumcheck", + "slop-symmetric", + "slop-tensor", + "slop-uni-stark", + "slop-whir", + "sp1-derive", + "sp1-primitives", + "struct-reflection", + "strum", + "thiserror 1.0.69", + "thousands", + "tokio", + "tracing", +] + +[[package]] +name = "sp1-jit" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "dynasmrt", + "hashbrown 0.14.5", + "libc", + "memfd", + "memmap2", + "serde", + "sp1-primitives", + "tracing", + "uuid", +] + +[[package]] +name = "sp1-primitives" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "bincode", + "blake3", + "elf", + "hex", + "itertools 0.14.0", + "lazy_static", + "num-bigint 0.4.6", + "serde", + "sha2", + "slop-algebra", + "slop-bn254", + "slop-challenger", + "slop-koala-bear", + "slop-poseidon2", + "slop-primitives", + "slop-symmetric", +] + +[[package]] +name = "sp1-prover" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "anyhow", + "bincode", + "clap", + "dirs", + "either", + "enum-map", + "eyre", + "futures", + "hashbrown 0.14.5", + "hex", + "indicatif", + "itertools 0.14.0", + "lru", + "mti", + "num-bigint 0.4.6", + "opentelemetry", + "pin-project", + "rand 0.8.6", + "reqwest", + "serde", + "serde_json", + "serial_test", + "sha2", + "slop-air", + "slop-algebra", + "slop-basefold", + "slop-bn254", + "slop-challenger", + "slop-futures", + "slop-jagged", + "slop-multilinear", + "slop-stacked", + "slop-symmetric", + "sp1-core-executor", + "sp1-core-executor-runner", + "sp1-core-machine", + "sp1-derive", + "sp1-hypercube", + "sp1-jit", + "sp1-primitives", + "sp1-prover-types", + "sp1-recursion-circuit", + "sp1-recursion-compiler", + "sp1-recursion-executor", + "sp1-recursion-gnark-ffi", + "sp1-recursion-machine", + "sp1-verifier", + "static_assertions", + "sysinfo", + "tempfile", + "thiserror 1.0.69", + "tokio", + "tonic", + "tracing", + "tracing-appender", + "tracing-subscriber", +] + +[[package]] +name = "sp1-prover-types" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "anyhow", + "async-scoped", + "bincode", + "chrono", + "futures-util", + "hashbrown 0.14.5", + "mti", + "prost", + "serde", + "sp1-core-machine", + "sp1-hypercube", + "sp1-primitives", + "tokio", + "tonic", + "tonic-build", + "tracing", +] + +[[package]] +name = "sp1-recursion-circuit" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "bincode", + "itertools 0.14.0", + "rand 0.8.6", + "rayon", + "serde", + "slop-air", + "slop-algebra", + "slop-alloc", + "slop-basefold", + "slop-basefold-prover", + "slop-bn254", + "slop-challenger", + "slop-commit", + "slop-jagged", + "slop-koala-bear", + "slop-matrix", + "slop-merkle-tree", + "slop-multilinear", + "slop-stacked", + "slop-sumcheck", + "slop-symmetric", + "slop-tensor", + "slop-whir", + "sp1-core-executor", + "sp1-core-machine", + "sp1-derive", + "sp1-hypercube", + "sp1-primitives", + "sp1-recursion-compiler", + "sp1-recursion-executor", + "sp1-recursion-machine", + "tracing", +] + +[[package]] +name = "sp1-recursion-compiler" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "backtrace", + "cfg-if", + "itertools 0.14.0", + "serde", + "slop-algebra", + "slop-bn254", + "slop-symmetric", + "sp1-core-machine", + "sp1-hypercube", + "sp1-primitives", + "sp1-recursion-executor", + "tracing", + "vec_map", +] + +[[package]] +name = "sp1-recursion-executor" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "backtrace", + "cfg-if", + "hashbrown 0.14.5", + "itertools 0.14.0", + "range-set-blaze", + "serde", + "slop-algebra", + "slop-maybe-rayon", + "slop-poseidon2", + "slop-symmetric", + "smallvec", + "sp1-derive", + "sp1-hypercube", + "static_assertions", + "thiserror 1.0.69", + "tracing", +] + +[[package]] +name = "sp1-recursion-gnark-ffi" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "anyhow", + "bincode", + "cfg-if", + "hex", + "num-bigint 0.4.6", + "serde", + "serde_json", + "sha2", + "slop-algebra", + "slop-symmetric", + "sp1-hypercube", + "sp1-primitives", + "sp1-recursion-compiler", + "sp1-verifier", + "tempfile", + "tracing", +] + +[[package]] +name = "sp1-recursion-machine" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "itertools 0.14.0", + "rand 0.8.6", + "slop-air", + "slop-algebra", + "slop-basefold", + "slop-matrix", + "slop-maybe-rayon", + "slop-symmetric", + "sp1-derive", + "sp1-hypercube", + "sp1-primitives", + "sp1-recursion-executor", + "strum", + "tracing", +] + +[[package]] +name = "sp1-sdk" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "anyhow", + "async-trait", + "bincode", + "cfg-if", + "dirs", + "eventsource-stream", + "futures", + "hex", + "indicatif", + "itertools 0.14.0", + "k256", + "num-bigint 0.4.6", + "serde", + "sha2", + "sp1-build", + "sp1-core-executor", + "sp1-core-executor-runner", + "sp1-core-machine", + "sp1-cuda", + "sp1-hypercube", + "sp1-primitives", + "sp1-prover", + "sp1-prover-types", + "sp1-recursion-executor", + "sp1-verifier", + "strum", + "tempfile", + "thiserror 1.0.69", + "tokio", + "tracing", +] + +[[package]] +name = "sp1-verifier" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "bincode", + "blake3", + "cfg-if", + "dirs", + "hex", + "lazy_static", + "serde", + "sha2", + "slop-algebra", + "slop-challenger", + "slop-primitives", + "slop-symmetric", + "sp1-hypercube", + "sp1-primitives", + "sp1-recursion-executor", + "sp1-recursion-machine", + "strum", + "substrate-bn-succinct-rs", + "thiserror 2.0.18", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strength_reduce" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "struct-reflection" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "701b671d1ad68e250e05718f95dae3014a17f4e69cbe51842531c30495ff3301" +dependencies = [ + "struct-reflection-derive", +] + +[[package]] +name = "struct-reflection-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ab74230a0592602e361bd63c645413fa8cbe4500d10274e849179e5c72548f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "subenum" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee3fb942ed39f3971438fcc7e05e20717e599e14c5c7cb50edd0df2a44b274" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "substrate-bn-succinct-rs" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a241fd7c1016fb8ad30fcf5a20986c0c4538e8f15a1b41a1761516299e377ec1" +dependencies = [ + "bytemuck", + "byteorder", + "cfg-if", + "crunchy", + "lazy_static", + "num-bigint 0.4.6", + "rand 0.8.6", + "rustc-hex", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "symlink" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "sysinfo" +version = "0.30.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "rayon", + "windows", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "thousands" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820" + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.6.3", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.14.0", + "toml_datetime 0.6.11", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.25.11+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" +dependencies = [ + "indexmap 2.14.0", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "winnow 1.0.3", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow 1.0.3", +] + +[[package]] +name = "tonic" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64", + "bytes", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "prost", + "rustls-pemfile", + "socket2 0.5.10", + "tokio", + "tokio-rustls", + "tokio-stream", + "tower 0.4.13", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-build" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "prost-types", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand 0.8.6", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower 0.5.3", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-appender" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "050686193eb999b4bb3bc2acfa891a13da00f79734704c4b8b4ef1a10b368a3c" +dependencies = [ + "crossbeam-channel", + "symlink", + "thiserror 2.0.18", + "time", + "tracing-subscriber", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-forest" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee40835db14ddd1e3ba414292272eddde9dad04d3d4b65509656414d1c42592f" +dependencies = [ + "ansi_term", + "smallvec", + "thiserror 1.0.69", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "transpose" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad61aed86bc3faea4300c7aee358b4c6d0c8d6ccc36524c96e4c92ccf26e77e" +dependencies = [ + "num-integer", + "strength_reduce", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "twox-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c" + +[[package]] +name = "typeid_prefix" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9da1387307fdee46aa441e4f08a1b491e659fcac1aca9cd71f2c624a0de5d1b" + +[[package]] +name = "typeid_suffix" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77b55e96f110c6db5d1a2f24072552537f0091dc90cebeaa679540bac93e7405" +dependencies = [ + "uuid", +] + +[[package]] +name = "typenum" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" +dependencies = [ + "atomic", + "getrandom 0.4.2", + "js-sys", + "md-5", + "sha1_smol", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +dependencies = [ + "serde", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.117", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap 2.14.0", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap 2.14.0", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core 0.52.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap 2.14.0", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap 2.14.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.14.0", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yoke" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/sp1/Cargo.toml b/sp1/Cargo.toml new file mode 100644 index 000000000..91757491b --- /dev/null +++ b/sp1/Cargo.toml @@ -0,0 +1,6 @@ +[workspace] +members = ["host"] +# `guest/` is built via `sp1-build` for the RISC-V zkVM target and only +# compiles under `cargo-prove`; excluded so host workspace ops don't pick it up. +exclude = ["guest"] +resolver = "2" diff --git a/sp1/guest/Cargo.lock b/sp1/guest/Cargo.lock new file mode 100644 index 000000000..dc3f1e05d --- /dev/null +++ b/sp1/guest/Cargo.lock @@ -0,0 +1,1272 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addchain" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e33f6a175ec6a9e0aca777567f9ff7c3deefc255660df887e7fa3585e9801d8" +dependencies = [ + "num-bigint 0.3.3", + "num-integer", + "num-traits", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "bignat" +version = "0.1.0" +source = "git+https://github.com/argumentcomputer/lean-ffi.git?rev=839b405de708b80504fda39abe1402114b4135a5#839b405de708b80504fda39abe1402114b4135a5" +dependencies = [ + "num-bigint 0.4.6", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake3" +version = "1.8.5" +source = "git+https://github.com/argumentcomputer/BLAKE3?branch=sp1#d36366f7badbff9be8e2522868dddd14561638f3" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "cpufeatures 0.3.0", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.2.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "const-default" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b396d1f76d455557e1218ec8066ae14bba60b4b36ecd55577ba979f5db7ecaa" + +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "dashmap" +version = "6.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6361d5c062261c78a176addb82d4c821ae42bed6089de0e12603cd25de2059c" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", + "rayon", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "elf" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b" + +[[package]] +name = "embedded-alloc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f2de9133f68db0d4627ad69db767726c99ff8585272716708227008d3f1bddd" +dependencies = [ + "const-default", + "critical-section", + "linked_list_allocator", + "rlsf", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "bitvec", + "byteorder", + "ff_derive", + "rand_core", + "subtle", +] + +[[package]] +name = "ff_derive" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f10d12652036b0e99197587c6ba87a8fc3031986499973c030d8b44fcc151b60" +dependencies = [ + "addchain", + "num-bigint 0.3.3", + "num-integer", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "gcd" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "ix-common" +version = "0.1.0" +dependencies = [ + "bignat", + "blake3", + "indexmap", + "rustc-hash", +] + +[[package]] +name = "ix-kernel" +version = "0.1.0" +dependencies = [ + "bignat", + "blake3", + "dashmap", + "indexmap", + "itertools 0.14.0", + "ix-common", + "ixon", + "log", + "num-bigint 0.4.6", + "rayon", + "rustc-hash", +] + +[[package]] +name = "ixon" +version = "0.1.0" +dependencies = [ + "bignat", + "blake3", + "dashmap", + "indexmap", + "ix-common", + "memmap2", + "num-bigint 0.4.6", + "rayon", + "rustc-hash", + "sha2", + "tiny-keccak", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "linked_list_allocator" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b23ac50abb8261cb38c6e2a7192d3302e0836dac1628f6a93b82b4fad185897" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "memmap2" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +dependencies = [ + "libc", +] + +[[package]] +name = "num-bigint" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "p3-bn254-fr" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2077757c7cb514202ccb5368f521f23f5709c720599e6545c683c66e0a52d2d8" +dependencies = [ + "ff", + "num-bigint 0.4.6", + "p3-field", + "p3-poseidon2", + "p3-symmetric", + "rand", + "serde", +] + +[[package]] +name = "p3-challenger" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6a908924d43e4cfb93fb41c8346cac211b70314385a9037e9241f5b7f3eaf77" +dependencies = [ + "p3-field", + "p3-maybe-rayon", + "p3-symmetric", + "p3-util", + "serde", + "tracing", +] + +[[package]] +name = "p3-dft" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be6408b10a2c27eb13a7d5580c546c2179a8dc7dbc10a990657311891f9b41c0" +dependencies = [ + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-util", + "tracing", +] + +[[package]] +name = "p3-field" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dc75969ca3ac847f43e632ab979d59ff7a68f9eac8dbf8edcbba47fc2e1d3aa" +dependencies = [ + "itertools 0.12.1", + "num-bigint 0.4.6", + "num-traits", + "p3-util", + "rand", + "serde", +] + +[[package]] +name = "p3-koala-bear" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a9683cd0ef68100df7c62490533047bcf19c04c4a0fa1efc9d7c1e03e31f6b3" +dependencies = [ + "cfg-if", + "num-bigint 0.4.6", + "p3-field", + "p3-mds", + "p3-poseidon2", + "p3-symmetric", + "rand", + "rustc_version", + "serde", +] + +[[package]] +name = "p3-matrix" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75c3f150ceb90e09539413bf481e618d05ee19210b4e467d2902eb82d2e15281" +dependencies = [ + "itertools 0.12.1", + "p3-field", + "p3-maybe-rayon", + "p3-util", + "rand", + "serde", + "tracing", +] + +[[package]] +name = "p3-maybe-rayon" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0641952b42da45e1dfa2d4a2a3163e330f944ad9740942f35026c0a71a605f1" + +[[package]] +name = "p3-mds" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa4a5f250e174dcfca5cbeac6ad75713924e7e7320e0a335e3c50b8b1f4fe8ec" +dependencies = [ + "itertools 0.12.1", + "p3-dft", + "p3-field", + "p3-matrix", + "p3-symmetric", + "p3-util", + "rand", +] + +[[package]] +name = "p3-poseidon2" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "522986377b2164c5f94f2dae88e0e0a3d169cc6239202ef4aeb4322d60feffd0" +dependencies = [ + "gcd", + "p3-field", + "p3-mds", + "p3-symmetric", + "rand", + "serde", +] + +[[package]] +name = "p3-symmetric" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9047ce85c086a9b3f118e10078f10636f7bfeed5da871a04da0b61400af8793a" +dependencies = [ + "itertools 0.12.1", + "p3-field", + "serde", +] + +[[package]] +name = "p3-util" +version = "0.4.3-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff962f8eaa5f36e0447cee7c241f6b4b475fadf3ee61f154327a26bb4e009ba" +dependencies = [ + "serde", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rlsf" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1646a59a9734b8b7a0ac51689388a60fe1625d4b956348e9de07591a1478457a" +dependencies = [ + "cfg-if", + "const-default", + "libc", + "rustversion", + "svgbobdoc", +] + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "slop-algebra" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "itertools 0.14.0", + "p3-field", + "serde", +] + +[[package]] +name = "slop-bn254" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "ff", + "p3-bn254-fr", + "serde", + "slop-algebra", + "slop-challenger", + "slop-poseidon2", + "slop-symmetric", +] + +[[package]] +name = "slop-challenger" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "futures", + "p3-challenger", + "serde", + "slop-algebra", + "slop-symmetric", +] + +[[package]] +name = "slop-koala-bear" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "lazy_static", + "p3-koala-bear", + "serde", + "slop-algebra", + "slop-challenger", + "slop-poseidon2", + "slop-symmetric", +] + +[[package]] +name = "slop-poseidon2" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "p3-poseidon2", +] + +[[package]] +name = "slop-primitives" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "slop-algebra", +] + +[[package]] +name = "slop-symmetric" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "p3-symmetric", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "sp1-guest" +version = "0.1.0" +dependencies = [ + "ix-common", + "ix-kernel", + "ixon", + "sp1-zkvm", +] + +[[package]] +name = "sp1-lib" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "bincode", + "serde", + "sp1-primitives", +] + +[[package]] +name = "sp1-primitives" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "bincode", + "blake3", + "elf", + "hex", + "itertools 0.14.0", + "lazy_static", + "num-bigint 0.4.6", + "serde", + "sha2", + "slop-algebra", + "slop-bn254", + "slop-challenger", + "slop-koala-bear", + "slop-poseidon2", + "slop-primitives", + "slop-symmetric", +] + +[[package]] +name = "sp1-zkvm" +version = "6.2.2" +source = "git+https://github.com/argumentcomputer/sp1?branch=blake3-precompile#ab7cda20b0e2f892bb5c5cb0ccf5c1ce5ba12c6b" +dependencies = [ + "cfg-if", + "critical-section", + "embedded-alloc", + "getrandom 0.2.17", + "getrandom 0.3.4", + "lazy_static", + "libm", + "rand", + "sha2", + "sp1-lib", + "sp1-primitives", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "svgbobdoc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2c04b93fc15d79b39c63218f15e3fdffaa4c227830686e3b7c5f41244eb3e50" +dependencies = [ + "base64", + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-width", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] diff --git a/sp1/guest/Cargo.toml b/sp1/guest/Cargo.toml new file mode 100644 index 000000000..9caf05403 --- /dev/null +++ b/sp1/guest/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "sp1-guest" +version = "0.1.0" +edition = "2024" + +[dependencies] +ixon = { path = "../../crates/ixon" } +ix-kernel = { path = "../../crates/kernel" } +ix-common = { path = "../../crates/common" } +sp1-zkvm = { git = "https://github.com/argumentcomputer/sp1", branch = "blake3-precompile" } + +# Force blake3 to the patched copy that routes `compress_in_place` / +# `compress_xof` through SP1's BLAKE3_COMPRESS precompile on the zkVM target. +# The guest's transitive blake3 (via ix-kernel) would otherwise resolve to +# the registry version because `sp1-build` doesn't extend the host workspace. +[patch.crates-io] +blake3 = { git = "https://github.com/argumentcomputer/BLAKE3", branch = "sp1" } diff --git a/sp1/guest/src/main.rs b/sp1/guest/src/main.rs new file mode 100644 index 000000000..1ebee5aa8 --- /dev/null +++ b/sp1/guest/src/main.rs @@ -0,0 +1,226 @@ +//! SP1 guest binary: prove that every kernel-checkable constant in a +//! serialized Ixon environment typechecks under the Ix kernel. +//! +//! Input (three reads, in order): +//! 1. `u8`: kernel mode flag (0 = Anon default, 1 = Meta) via `read::`. +//! 2. `Vec`: Ixon-serialized environment via `read_vec`. +//! 3. `Vec`: optional check-list — a packed set of primary addresses +//! (`Address::pack`) to check in Anon mode. Empty ⇒ whole env; non-empty +//! ⇒ check exactly those work items (the `--only-const` path, where the +//! host ships a closure sub-env plus the one targeted primary). Mirrors +//! the Zisk leaf guest's reuse-mode selection. +//! +//! Output (via `sp1_zkvm::io::commit_slice`, fixed 104-byte layout): +//! - `u32` failures (offset 0; 0 = all pass) +//! - `[u8; 32]` subject_root — canonical merkle root over the consts +//! this proof certified (Anon mode; zero in Meta) +//! - `[u8; 32]` assumptions_root — merkle root over their direct refs not +//! certified here (the conditional claim's external assumptions) +//! - `u32` checked_count +//! - `[u8; 32]` env_hash +//! +//! `(subject_root, assumptions_root)` form `Claim::CheckEnv { root, assumptions }` +//! — the same content-addressed conditional claim the Zisk leaf guest emits. +//! +//! Anon mode enumerates the work set in-guest via +//! [`ix_kernel::anon_work::build_anon_work`] — the same enumeration +//! that drives `rs_kernel_check_anon` in `crates/ffi/src/kernel.rs`. +//! The proof commits to "every kernel-checkable constant in this env +//! typechecks," matching Aiur's `kernel_check_test` semantics. +//! +//! Meta mode walks `env.named` for the kid set (preserves Lean names +//! + runs the dup-level-param-name check); slightly more constrained +//! and slightly more expensive in cycles. +//! +//! Both modes typecheck the same underlying structural set; they +//! differ only in which metadata fields the kernel carries through +//! `KEnv`. + +#![no_main] +sp1_zkvm::entrypoint!(main); + +use ix_common::address::Address; +use ix_kernel::anon_work::{AnonWorkItem, build_anon_work}; +use ix_kernel::env::KEnv; +use ix_kernel::id::KId; +use ix_kernel::ingress::ixon_ingress_owned; +use ix_kernel::mode::{Anon, Meta}; +use ix_kernel::tc::TypeChecker; +use ixon::env::Env as IxonEnv; +use ixon::merkle::{merkle_root_canonical, zero_address}; +use ixon::metadata::ConstantMetaInfo; + +// SP1's cycle tracker (gated by the SDK `profiling` feature on the host +// side — see `sp1/host/Cargo.toml`) consumes `println!`s of the form +// `cycle-tracker-report-{start,end}: ` from stdout and accumulates +// total cycles per label across all invocations of that label. Each +// `println!` is a single `syscall_write`, so per-iteration brackets in +// hot loops add measurable overhead — keep these at coarse phase +// boundaries. To get per-constant cycle attribution (e.g. for a one-off +// "which `kid_NNN` dominates?" investigation), temporarily wrap the +// `tc.check_const(kid)` call below in the same pair of `println!`s; pair +// the output with a native `check_const` run over the same anon-work order +// to map indices to constant names. +macro_rules! tic { + ($name:expr) => { + println!("cycle-tracker-report-start: {}", $name) + }; +} +macro_rules! toc { + ($name:expr) => { + println!("cycle-tracker-report-end: {}", $name) + }; +} + +pub fn main() { + let meta_mode: u8 = sp1_zkvm::io::read(); + let env_bytes: Vec = sp1_zkvm::io::read_vec(); + // Optional check-list: a packed set of primary addresses to check (Anon + // mode). Empty ⇒ whole env. Ignored in Meta mode (the host forbids + // `--only-const` there). + let check_list: Vec = sp1_zkvm::io::read_vec(); + // `Address::hash` is blake3 via the precompile shim (same path the kernel + // uses) — binds the proof to the exact env payload. + let env_hash: [u8; 32] = *Address::hash(&env_bytes).as_bytes(); + + // Conditional-claim outputs: populated in Anon mode (the claim/resolution + // path, mirroring the Zisk leaf guest); left zero in Meta mode. + let mut subject_root = zero_address(); + let mut assumptions_root = zero_address(); + let mut checked_count: u32 = 0; + + let failures = if meta_mode == 1 { + // ---- Meta mode: full env load + eager ingress ---- + // + // `ixon_ingress_inner` walks `env.named` to enumerate work items, + // so we need the full env (with the metadata sections intact). + tic!("ixon_decode_env"); + let env = + IxonEnv::get(&mut &env_bytes[..]).expect("invalid Ixon environment"); + toc!("ixon_decode_env"); + + let mut kids: Vec> = Vec::with_capacity(env.named.len()); + kids.extend( + env + .named + .iter() + .filter(|e| { + !matches!(e.value().meta.info, ConstantMetaInfo::Muts { .. }) + }) + .map(|e| KId::::new(e.value().addr.clone(), e.key().clone())), + ); + + tic!("ingress"); + let (mut kenv, _intern) = + ixon_ingress_owned::(env).expect("ingress failed"); + toc!("ingress"); + + let mut failures: u32 = 0; + tic!("check_const_loop"); + let mut tc = TypeChecker::new(&mut kenv); + for kid in &kids { + if tc.check_const(kid).is_err() { + failures = failures.saturating_add(1); + } + } + toc!("check_const_loop"); + failures + } else { + // ---- Anon mode: get_anon + lazy on-demand ingress ---- + // + // `Env::get_anon` parses-and-discards the metadata sections + // (named/names/comms) so the steady-state env carries only + // `consts`/`blobs`/`anon_hints`. `build_anon_work` enumerates the + // canonical kernel-checkable work set from `env.consts` alone + // (no metadata) — same logic the FFI's `rs_kernel_check_anon` + // uses. Each `tc.check_const(primary)` either typechecks one + // standalone or drives the kernel's block coordination to cover + // every member + ctor of a Muts block. + tic!("ixon_decode_env"); + let env = + IxonEnv::get_anon(&mut &env_bytes[..]).expect("invalid Ixon environment"); + toc!("ixon_decode_env"); + + tic!("build_anon_work"); + let work = build_anon_work(&env).expect("build_anon_work"); + toc!("build_anon_work"); + + // Empty check-list ⇒ whole env. Non-empty ⇒ check exactly the work items + // whose primary is in the set (the `--only-const` path). `Address` orders + // by raw bytes, so sort + binary_search is correct — mirrors the Zisk leaf + // guest's reuse-mode selection. + let items_to_check: Vec<&AnonWorkItem> = if check_list.is_empty() { + work.iter().collect() + } else { + let mut want: Vec
= Address::unpack(&check_list).collect(); + want.sort_unstable(); + work + .iter() + .filter(|it| want.binary_search(it.primary()).is_ok()) + .collect() + }; + + let mut kenv = KEnv::::new(); + let mut failures: u32 = 0; + let mut checked_covered: Vec
= Vec::new(); + tic!("check_const_loop"); + let mut tc = TypeChecker::::new_with_lazy_anon(&mut kenv, &env); + for item in &items_to_check { + let kid = KId::::new(item.primary().clone(), ()); + if tc.check_const(&kid).is_err() { + failures = failures.saturating_add(1); + } + // Certify in `consts`-key space (block addr + projections) — the same + // address space `Constant.refs` use, so the assumption set and its + // cross-proof resolution line up. + checked_covered.extend(item.proven_targets()); + } + toc!("check_const_loop"); + + // ---- Conditional claim: CheckEnv { subject, assumptions } ---- + // + // subject = canonical merkle root over the consts certified here. + // assumptions = canonical merkle root over their DIRECT refs not + // certified here — this whole-env proof's external dependencies + // (ultimately the declared axioms). Mirrors the Zisk leaf guest so + // both backends emit the same content-addressed claim. + // + // A `Constant.refs` entry points at either another CONSTANT (a genuine + // typing obligation) or a literal DATA blob (the bytes behind an + // `Expr::Nat`/`Expr::Str` — e.g. the numbers 0, 1). Literals are + // well-typed by construction with content-pinned bytes, so they are NOT + // assumptions; skip refs that resolve to a blob (must match Zisk). + tic!("claim_roots"); + checked_count = checked_covered.len() as u32; + subject_root = + merkle_root_canonical(&checked_covered).unwrap_or_else(zero_address); + let mut sorted = checked_covered.clone(); + sorted.sort_unstable(); + sorted.dedup(); + let mut assumptions: Vec
= Vec::new(); + for t in &checked_covered { + if let Some(c) = env.get_const(t) { + for r in &c.refs { + if sorted.binary_search(r).is_err() && env.get_blob(r).is_none() { + assumptions.push(r.clone()); + } + } + } + } + assumptions_root = + merkle_root_canonical(&assumptions).unwrap_or_else(zero_address); + toc!("claim_roots"); + failures + }; + + // Fixed 104-byte public output: failures(4) + subject_root(32) + + // assumptions_root(32) + checked_count(4) + env_hash(32). `failures` stays + // at offset 0 so existing readers keep working. + let mut out = Vec::with_capacity(104); + out.extend_from_slice(&failures.to_le_bytes()); + out.extend_from_slice(subject_root.as_bytes()); + out.extend_from_slice(assumptions_root.as_bytes()); + out.extend_from_slice(&checked_count.to_le_bytes()); + out.extend_from_slice(&env_hash); + sp1_zkvm::io::commit_slice(&out); +} diff --git a/sp1/host/Cargo.toml b/sp1/host/Cargo.toml new file mode 100644 index 000000000..f45c1d3c5 --- /dev/null +++ b/sp1/host/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "sp1-host" +version = "0.1.0" +edition = "2024" +default-run = "sp1-host" + +[[bin]] +name = "sp1-host" +path = "src/main.rs" + +[dependencies] +ixon = { path = "../../crates/ixon" } +ix-common = { path = "../../crates/common" } +# Used only to count work items for human-readable output via +# `build_anon_work` — same enumeration the guest runs in-circuit. +ix-kernel = { path = "../../crates/kernel" } +# `profiling` turns on the SP1 executor's cycle-tracker: it reads +# `cycle-tracker-report-{start,end}: