Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .claude/settings.json
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
{"includeCoAuthoredBy":false,"attribution":{"co_authored_by":false,"commit_message_footer":false}}
{
"includeCoAuthoredBy": false,
"attribution": {
"co_authored_by": false,
"commit_message_footer": false
}
}
2 changes: 1 addition & 1 deletion .github/workflows/prune.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ permissions:
# pins.yml issue body lists every site.
env:
PINNED_TOOLCHAIN: nightly-2026-04-03
RECONVERGE_VERSION: "0.1.11"
RECONVERGE_VERSION: "0.3.0"

jobs:
gate:
Expand Down
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,37 @@ change measured timings are marked `bench:`.

## [Unreleased]

## [1.2.0] - 2026-08-22

### Changed

- **The safety gate now pins `reconverge` 0.3.0**, up from 0.1.11 — two minor
versions of analyzer the gate was not getting. The pin moves in four places:
the corpus workflow, the Action's `reconverge-version` default, and the two
documents that name it.

**The gate admits exactly the same set.** A newer analyzer can change what
the gate refuses, which is a change in product behaviour rather than a
dependency bump, so the corpus was re-run under both versions on the same
toolchain and compared: **93 clean, 8 refused, 0 caveats, 0 tool errors**,
and the two runs are **byte-identical** — same candidate hashes, same
`REFUSED RC001` lines, same reasons. The eight refusals are `reduce-flip`
above one warp, which is the corpus's known flip and the behaviour the gate
exists to produce. The measurement is recorded in
[docs/research-baseline.md](docs/research-baseline.md#analyzer-equivalence-0111--030).

No toolchain change was needed: launchbound and reconverge 0.3.0 already pin
the same `nightly-2026-04-03`, so the rule that the analyzer and the
toolchain move together is satisfied without moving either.

What this does *not* establish is general equivalence. reconverge gained
multi-warp replay, bounded inlining and unmasked warp-wrapper analysis
between these versions; a kernel exercising those paths could be decided
differently. Six kernels are the evidence, and six kernels are what they are.

- `docs/LIMITATIONS.md` now describes the limits of the analyzer the gate
actually runs, and carries the date it was re-checked.

## [1.1.0] - 2026-08-22

### Added
Expand Down
22 changes: 11 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 9 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ members = [
exclude = ["corpus"]

[workspace.package]
version = "1.1.0"
version = "1.2.0"
edition = "2024"
# MSRV for crates that do not require the pinned nightly (CONTRIBUTING.md);
# set by ratatui 0.30. The analysis and compile paths require
Expand All @@ -33,14 +33,14 @@ keywords = ["cuda", "gpu", "autotuner", "convergence", "kernel"]
categories = ["development-tools", "development-tools::profiling"]

[workspace.dependencies]
launchbound-space = { path = "crates/launchbound-space", version = "1.1.0" }
launchbound-prune = { path = "crates/launchbound-prune", version = "1.1.0" }
launchbound-build = { path = "crates/launchbound-build", version = "1.1.0" }
launchbound-bench = { path = "crates/launchbound-bench", version = "1.1.0" }
launchbound-report = { path = "crates/launchbound-report", version = "1.1.0" }
launchbound-search = { path = "crates/launchbound-search", version = "1.1.0" }
launchbound-model = { path = "crates/launchbound-model", version = "1.1.0" }
launchbound-metal = { path = "crates/launchbound-metal", version = "1.1.0" }
launchbound-space = { path = "crates/launchbound-space", version = "1.2.0" }
launchbound-prune = { path = "crates/launchbound-prune", version = "1.2.0" }
launchbound-build = { path = "crates/launchbound-build", version = "1.2.0" }
launchbound-bench = { path = "crates/launchbound-bench", version = "1.2.0" }
launchbound-report = { path = "crates/launchbound-report", version = "1.2.0" }
launchbound-search = { path = "crates/launchbound-search", version = "1.2.0" }
launchbound-model = { path = "crates/launchbound-model", version = "1.2.0" }
launchbound-metal = { path = "crates/launchbound-metal", version = "1.2.0" }
anyhow = "1"
clap = { version = "4", features = ["derive"] }
serde = { version = "1", features = ["derive"] }
Expand Down
2 changes: 1 addition & 1 deletion action/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ inputs:
reconverge-driver). Must be built by the toolchain below — the pins
move together.
required: false
default: "0.1.11"
default: "0.3.0"
toolchain:
description: >-
The pinned nightly that matches reconverge-version; the pair moves
Expand Down
4 changes: 2 additions & 2 deletions docs/LIMITATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

A tool that overstates its reach is worse than one that does less. These are
launchbound's, with numbers where we have them. Everything here was true on
2026-08-20 against the pins in rust-toolchain.toml and CONTRIBUTING.md.
2026-08-22 against the pins in rust-toolchain.toml and CONTRIBUTING.md.

## The gate inherits reconverge's limits, wholesale

A clean gate is **not a proof of correctness**. `reconverge` (v0.1.11) is
A clean gate is **not a proof of correctness**. `reconverge` (v0.3.0) is
summary-based and interprocedural, handles reducible control flow only,
cannot evaluate non-literal masks, and puts data races entirely out of
scope. Its own documentation is the authority; launchbound adds no analysis
Expand Down
43 changes: 42 additions & 1 deletion docs/research-baseline.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Measured 2026-08-20.
| tier-1 guest | Apple `container` 1.2.0, **native arm64** Ubuntu 24.04 (no Docker, no Rosetta — operator requirement), CUDA toolkit 13.2 (sbsa), LLVM 21.1.8, container `cuda-oxide-dev` |
| tier-2 box | AWS `g5.xlarge` spot @ **$0.364/hr**, us-east-2c, **NVIDIA A10G** (`sm_86`, cc 8.6), driver 595.71.05, CUDA 13.2 (V13.2.51), LLVM 21.1.8 — chosen over the T4 by the operator; barely above T4 spot ($0.335/hr) |
| pinned toolchain | `nightly-2026-04-03` (`rustc 1.96.0-nightly (55e86c996 2026-04-02)`) |
| reconverge | `cargo-reconverge 0.1.11` (built at `~/Projects/reconverge/target/release`) |
| reconverge | `cargo-reconverge 0.1.11` (built at `~/Projects/reconverge/target/release`) — **the version these measurements were taken with**; the gate now pins 0.3.0, which was verified to admit the identical set (see below) |
| cuda-oxide | checkout `50d07314eb8b7d5ec821ba02b0048a753c20dd4e` — the tree synced to the box (the box AMI's own stale clone reports `e28248c1`, but `./gpu sync` replaces the working tree and excludes `.git`, so the synced tree is what compiled) |
| subject kernels | `s0-reduce` (device-only lib crate, dep `cuda-device` only, containing the README's known-flip reduction); cuda-oxide examples `vecadd` (small) and `tiled_gemm` (large) |
| evidence logs | `~/Projects/cuda-oxide/.gpu-evidence/20260820T{071248,071807,071959}Z.log` |
Expand Down Expand Up @@ -131,3 +131,44 @@ Findings on the subject kernel (`--strict`, `--cc 7.5`):
|---|---|---|
| RC005 | warning | kernel `reduce_flip` calls `index_1d()` without a launch contract |
| RC001 | warning | kernel `reduce_flip` may execute `sync_threads()` under thread-divergent control |

## Analyzer equivalence: 0.1.11 → 0.3.0

The gate's pinned analyzer moved from `cargo-reconverge` 0.1.11 to 0.3.0. A
newer analyzer can change **what the gate admits**, which is a change in
product behaviour rather than a dependency bump — so the corpus was re-run
under both, on the same toolchain, and compared.

Measured 2026-08-22 on `nightly-2026-04-03`, cuda-oxide `50d07314`, tier 0
(no GPU):

```console
$ cargo run -q -p launchbound-cli -- prune --cc 8.6
```

| kernel | clean | caveats | refused | tool errors |
|---|---|---|---|---|
| histogram | 12 | 0 | 0 | 0 |
| matmul-tiled | 18 | 0 | 0 | 0 |
| reduce-flip | 3 | 0 | **8** | 0 |
| reduce-stable | 11 | 0 | 0 | 0 |
| scan-block | 4 | 0 | 0 | 0 |
| stencil-1d | 45 | 0 | 0 | 0 |
| **total** | **93** | **0** | **8** | **0** |

**The two runs are byte-identical** — not merely equal in the totals, but the
same candidate hashes, the same `REFUSED RC001` lines, the same reasons. The
eight refusals are the `reduce-flip` candidates at block sizes above one warp,
which is the corpus's known flip and the behaviour the gate exists to produce.

The gate tests pass under 0.3.0 unchanged, including
`known_flip_kernel_disqualifies_above_one_warp` and
`known_stable_kernel_disqualifies_nothing`.

**What this does and does not establish.** It establishes that on *this*
corpus, at cc 8.6, the two analyzers decide identically — so the bump carries
no behaviour change this project can observe. It does not establish that they
are equivalent in general: reconverge gained multi-warp replay, bounded
inlining and unmasked warp-wrapper analysis between these versions, and a
kernel exercising those paths could well be decided differently. The corpus is
the evidence, and the corpus is six kernels.
Loading