Skip to content

Commit 3f404c5

Browse files
authored
Merge pull request #359 from AdaWorldAPI/ccr-9f3b8075-74u47l-chain-reject
simd: withdraw mask_gather_chain_u32 (rejected design)
2 parents e726f98 + 56c4669 commit 3f404c5

4 files changed

Lines changed: 8 additions & 467 deletions

File tree

‎.claude/blackboard.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 2026-10-12 — `mask_gather_chain_u32` withdrawn: rejected design, moved to backup
2+
3+
- **Removed:** `mask_gather_chain_u32`, `mask_gather_chain_u32_under`, `ChainHop`, `ChainTest`, their tests and the parity case 0xD02/0xD03 (shipped in #358). The exact state is kept on branch `backup/session-011BMJ-2026-10-12`.
4+
- **Why it is wrong, not merely slow:** it treated a hop as a key read. Every anchor row walked every hop, each read's address being the value the previous read returned: scalar, dependent, proportional to rows × depth. The graph has no foreign keys; everything is Cartesian coordinates. A hop is a coordinate range on the sealed ordered address, and a chain of hops is the intersection of those ranges (`[max(lo), min(hi))`); no value is read as an address. lance-graph had already measured this before #358 was written: D-TELEPORT-0 (#1484) shows a gated gather accelerates work but does not eliminate it, while a range found at the crossing point eliminates it (COUNT `hi - lo`, ~28 ns). The crossword probe (D-PUZZLE-0) had also recorded the gather as "not needed: elimination is an AND within one space". Neither was read.
5+
- **No replacement here.** The rebuild uses what lance-graph's mask-risc and Quack already have (`Lowering::Range`, ternlog fusion over resident planes, `CountKeyRunsU32`, `fold_ranges`, `prefix_*`). A primitive is added to ndarray only if that rebuild measures a gap.
6+
17
## 2026-10-11 — `cpu_guard` no longer kills library hosts; three OOB paths and the AMX rung closed; fleet co-research map
28

39
- **Defect found and fixed (X1):** the pre-`main` hook from #348 runs in every artifact that links ndarray, a `cdylib` included. Measured: an `x86-64-v4` cdylib `dlopen`ed by a C host under `qemu -cpu Haswell` ended the host with 132. A JVM loading lance-graph-java's `.so`, or Python loading tesseract-ocr-python, would die the same way. The guard now exits only when its code lies in the main executable (ELF: object at `getauxval(AT_PHDR)`, FreeBSD `elf_aux_info`; macOS `dladdr` vs `_NSGetMachExecuteHeader()`; Windows `GetModuleHandleExW`); in a library it prints a `warning:` and returns. Every lookup failure answers "library".

‎crates/simd-masking-parity/src/lib.rs‎

Lines changed: 2 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,14 @@ use ndarray::simd::{
4141
ge_u8_to_mask, gt_i32_to_mask, gt_i32_to_mask_under, gt_u64_to_mask, gt_u8_to_mask, le_i32_to_mask,
4242
le_i32_to_mask_under, le_u64_to_mask, le_u8_to_mask, lt_i32_to_mask, lt_i32_to_mask_under, lt_u64_to_mask,
4343
lt_u8_to_mask, mask_all, mask_and, mask_and_assign, mask_andnot, mask_andnot_assign, mask_any,
44-
mask_gather_chain_u32, mask_gather_chain_u32_under, mask_gather_u32, mask_gather_u32_under, mask_not,
44+
mask_gather_u32, mask_gather_u32_under, mask_not,
4545
mask_not_assign, mask_or, mask_or_assign, mask_scatter_or_u32, mask_set_range, mask_shift_morton, mask_ternlog,
4646
mask_ternlog_any, mask_ternlog_assign, mask_ternlog_popcount, mask_xor, mask_xor_assign, masked_group_sum_i32,
4747
masked_group_sum_i32_via, masked_key_run_count_u32, masked_max_i32, masked_min_i32, masked_strided_group_sum,
4848
masked_sum_i32, masked_sum_wrapping_add_i32, ne_i32_to_mask, ne_i32_to_mask_under, ne_u32_to_mask,
4949
ne_u32_to_mask_under, ne_u64_to_mask, ne_u8_to_mask, ternary_match_strided16_to_mask,
5050
ternary_match_strided_to_mask, ternary_match_u32_to_mask, ternary_match_u32_to_mask_under,
51-
ternary_match_u64_to_mask, ternary_match_u64_to_mask_under, ternlog, ChainHop, ChainTest, F64x8, I32x16, I8x16,
51+
ternary_match_u64_to_mask, ternary_match_u64_to_mask_under, ternlog, F64x8, I32x16, I8x16,
5252
KeyRunCarry, MortonDir, U32x16, U64x8, U8x16,
5353
};
5454

@@ -1591,79 +1591,6 @@ fn check_gather_scatter_group() -> Result<(), u32> {
15911591
return Err(0xD01);
15921592
}
15931593

1594-
// ── mask_gather_chain_u32[_under] ────────────────────────────────
1595-
// Two hops: index -> mid (live + one test, keyed onward through
1596-
// `mid_next`) -> far (live + one negated test). The reference walks
1597-
// the chain row by row from the same lanes; out-of-range keys at
1598-
// both depths exercise the "false by contract" arm.
1599-
let mid_rows = 30usize;
1600-
let far_rows = 20usize;
1601-
let mid_live: Vec<u64> = (0..words_for(mid_rows)).map(|_| rng.next()).collect();
1602-
let far_live: Vec<u64> = (0..words_for(far_rows)).map(|_| rng.next()).collect();
1603-
let mid_lane: Vec<u32> = (0..mid_rows).map(|_| (rng.next() % 3) as u32).collect();
1604-
let far_lane: Vec<u32> = (0..far_rows).map(|_| (rng.next() % 3) as u32).collect();
1605-
let mid_next: Vec<u32> = (0..mid_rows)
1606-
.map(|j| {
1607-
if j % 7 == 0 {
1608-
(far_rows + 2) as u32
1609-
} else {
1610-
(rng.next() % far_rows as u64) as u32
1611-
}
1612-
})
1613-
.collect();
1614-
let cindex: Vec<u32> = (0..n)
1615-
.map(|i| {
1616-
if i % 5 == 0 {
1617-
(mid_rows + 1 + i) as u32
1618-
} else {
1619-
(rng.next() % mid_rows as u64) as u32
1620-
}
1621-
})
1622-
.collect();
1623-
let mid_tests = [ChainTest {
1624-
lane: &mid_lane,
1625-
value: 1,
1626-
negate: false,
1627-
}];
1628-
let far_tests = [ChainTest {
1629-
lane: &far_lane,
1630-
value: 2,
1631-
negate: true,
1632-
}];
1633-
let hops = [
1634-
ChainHop {
1635-
live: &mid_live,
1636-
rows: mid_rows,
1637-
tests: &mid_tests,
1638-
next: Some(&mid_next),
1639-
},
1640-
ChainHop {
1641-
live: &far_live,
1642-
rows: far_rows,
1643-
tests: &far_tests,
1644-
next: None,
1645-
},
1646-
];
1647-
let bit = |w: &[u64], k: usize| (w[k / 64] >> (k % 64)) & 1 == 1;
1648-
let holds = |i: usize| {
1649-
let a = cindex[i] as usize;
1650-
if a >= mid_rows || !bit(&mid_live, a) || mid_lane[a] != 1 {
1651-
return false;
1652-
}
1653-
let b = mid_next[a] as usize;
1654-
b < far_rows && bit(&far_live, b) && far_lane[b] != 2
1655-
};
1656-
let mut out = vec![u64::MAX; out_len];
1657-
mask_gather_chain_u32(&cindex, &hops, &mut out);
1658-
if out != reference_mask(n, out_len, holds) {
1659-
return Err(0xD02);
1660-
}
1661-
let mut out = vec![u64::MAX; out_len];
1662-
mask_gather_chain_u32_under(&cindex, &gate, &hops, &mut out);
1663-
if out != reference_mask(n, out_len, |i| bit(&gate, i) && holds(i)) {
1664-
return Err(0xD03);
1665-
}
1666-
16671594
// ── mask_scatter_or_u32 ──────────────────────────────────────────
16681595
// `out2` starts explicitly zeroed (the caller's job now, not the
16691596
// primitive's): the accumulate contract means a dirty prefill would

‎src/simd.rs‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -868,8 +868,6 @@ pub use crate::simd_masking_ops::{
868868
mask_andnot,
869869
mask_andnot_assign,
870870
mask_any,
871-
mask_gather_chain_u32,
872-
mask_gather_chain_u32_under,
873871
mask_gather_u32,
874872
mask_gather_u32_under,
875873
mask_not,
@@ -933,8 +931,6 @@ pub use crate::simd_masking_ops::{
933931
widen_bounded_cross_power_sums,
934932
widen_bounded_power_sums,
935933
BoundedFoldError,
936-
ChainHop,
937-
ChainTest,
938934
CrossPowerSums,
939935
KeyRunCarry,
940936
MortonDir,

0 commit comments

Comments
 (0)