Skip to content

codelib: words32/slot32 twins + verified u32 copy loop (#68 phase 2c) - #140

Merged
mfornet merged 7 commits into
cajal-technologies:mainfrom
theebayuser:codelib/mem-words32
Jul 16, 2026
Merged

codelib: words32/slot32 twins + verified u32 copy loop (#68 phase 2c)#140
mfornet merged 7 commits into
cajal-technologies:mainfrom
theebayuser:codelib/mem-words32

Conversation

@theebayuser

Copy link
Copy Markdown
Contributor

Overview

Phase 2c of #68: the 32-bit twins of the slot64/words64 region+view algebra, plus a verified word-by-word copy loop — the pieces the [u32] memory proofs (merge_sort, #106) need. See the comment on #106 for how this maps onto the Framing.lean/wordsAt lemmas that PR is currently carrying privately.

Stacked on #131 / #137 / #138 / #139 (review the last commit).

What's new

  • MemRegion.slot32 (Region.lean) — the k-th 4-byte element slot, with shl2_eq_mul4, slot32_base_toNat (no-wrap), and slot32_disjoint. Exact 4-byte twin of slot64.
  • Mem.words32 (MemArray.lean) — the List UInt32 view of [base, base+4n), defined in the same (List.range n).map … shape as #106's wordsAt (so that file can become an import), with length/getElem/ext/write32_outside/succ and a general words32_write32_snoc ("writing the n-th slot appends its value to the length-n view") that serves both copy and fill loops.
  • copyWords_spec (MemCopyLoop.lean) — the companion to codelib: words64 array view + first ∀-quantified loop-over-memory proof (#68 phase 2b) #139's fillWords_spec: the canonical load32/store32 copy loop over two disjoint u32 regions, proved for all n to leave the destination view equal to the (unchanged) source view:
    st'.mem.words32 dst n = st.mem.words32 src n
    
    This is what LLVM emits for a small copy_from_slice at opt-level = 0 (a load/store loop, not memory.copy), and the direct analogue of merge_sort's copy helpers. The loop invariant (words32 dst i = words32 src i, source region framed intact) is discharged with words32_write32_snoc on the destination and words32_write32_outside (via MemRegion disjointness) on the source — no take/index gymnastics.

Verification

  • lake build green in codelib/; zero lint warnings.
  • #print axioms copyWords_spec → standard axioms + the bv_decide reflection axioms from the shift/address lemmas; no sorryAx.
  • Consumers land with the code: slot32/words32/words32_write32_snoc/words32_write32_outside are all consumed by copyWords_spec.

Refs #68, #106.


Disclosure per CONTRIBUTING: AI tooling (Claude Code) was used to write and check these proofs; I own the change and am accountable for every line.

🤖 Generated with Claude Code

@theebayuser
theebayuser requested a review from mfornet as a code owner July 12, 2026 07:26
theebayuser and others added 2 commits July 14, 2026 15:19
…mutation

Phase 2a of cajal-technologies#68. Adds CodeLib.RustStd.Region on top of the byte-level
framing family in RustStd.Frame:

- MemRegion (base : UInt32, len : Nat) with a decidable Disjoint predicate
  over .toNat intervals — the same shape the Frame lemmas consume, so
  omega/decide keep discharging it on concrete slots and symbolic array
  addresses alike.
- Disjoint stores commute (write64/write64, write32/write32, mixed) —
  requested verbatim in cajal-technologies#68 and previously absent. Proved byte-pointwise via
  a new Mem.ext_bytes + write*_bytes_in, no bv_decide, axiom-clean.
- Disjoint → Frame bridges (read*_write*_of_region) for all four widths.
- MemRegion.slot64: the k-th u64 element slot, with no-wrap, codegen-shift
  and pairwise-disjointness lemmas.

Consumer: SwapElements/Spec.lean's local address block (shl3, elemAddr_of_shl,
elemAddr_toNat, elemAddr_disjoint) collapses to three one-line specialisations
of the slot64 lemmas (elemAddr ptr k is defeq (slot64 ptr k).base). The
registered SwapElementsSpec statement is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 2b of cajal-technologies#68, on top of the MemRegion algebra.

MemArray.lean — Mem.words64 base n : List UInt64, the list view of the u64
array [base, base+8n). Defined via List.range/map so length and indexing are
simp-lemmas; plus words64_ext, words64_write64_outside (framing through
MemRegion), words64_succ (snoc) and words64_write64_extend (the fill step as a
view equation). This is the codelib home for the memory-as-list view merge_sort
(cajal-technologies#106) carries privately as wordsAt.

MemFillLoop.lean — fillWords_spec: the canonical fill loop writes v to each of
the n u64 slots of [base, base+8n), proved for ALL n via the invariant/variant
loop rule (wp_loop_cons), with the whole-region result stated as
  st'.mem.words64 base n.toNat = List.replicate n.toNat v.
Every memory example under interpreter/Examples is a concrete native_decide
check because symbolic framing lives downstream here in CodeLib; this is the
first universally-quantified loop-over-memory theorem in the repo, and the
shape merge_sort's proof needs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@theebayuser
theebayuser force-pushed the codelib/mem-words32 branch from 8a53e4d to 9187214 Compare July 14, 2026 23:30
mfornet and others added 2 commits July 14, 2026 16:34
…use fixes

Follow-up to review of the words64 array view + fill-loop proof:

- fillWords_spec now carries a frame condition: every byte outside
  `[base, base+8n)` is preserved (postcondition + loop invariant), so the
  theorem composes with facts about neighbouring memory (the merge_sort shape).
- Extract `Mem.words64_slotAddr_toNat`, the shared no-wrap slot-address bridge,
  and use it in `words64_write64_outside` / `_extend` (was duplicated inline).
- FillWords' `(const 3) shl` step reuses `MemRegion.shl3_eq_mul8` instead of a
  fresh `bv_decide`.
- MemArray module docstring: drop the stale `words64_write64_set` reference
  (the lemma is `words64_write64_extend`) and correct the simp-lemma note
  (`getElem_words64` is intentionally not `@[simp]`).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 2c of cajal-technologies#68 — the 32-bit twins the [u32] memory proofs (merge_sort, cajal-technologies#106)
need, plus their first consumer.

- MemRegion.slot32 (+ shl2_eq_mul4, slot32_base_toNat, slot32_disjoint):
  the 4-byte element-slot twin of slot64.
- Mem.words32: the List UInt32 view of [base, base+4n), in the same
  (List.range n).map shape as cajal-technologies#106's private `wordsAt`, with
  length/getElem/ext/write32_outside/succ and a general words32_write32_snoc
  (write the n-th slot ⇒ append its value to the length-n view).
- copyWords_spec (MemCopyLoop.lean): the companion to cajal-technologies#139's fillWords_spec.
  The canonical load32/store32 copy loop over two disjoint u32 regions is
  proved, for all n, to leave `words32 dst n = words32 src n` with the source
  unchanged — what LLVM emits for a small opt-0 copy_from_slice, and the
  analogue of merge_sort's copy helpers. Invariant discharged via
  words32_write32_snoc (dst) + words32_write32_outside (src, MemRegion
  disjointness); axiom-clean.

Refs cajal-technologies#68, cajal-technologies#106.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@theebayuser
theebayuser force-pushed the codelib/mem-words32 branch from 9187214 to b70fe5e Compare July 14, 2026 23:47
theebayuser and others added 3 commits July 14, 2026 17:54
…reuse

Applies the same review pattern mfornet landed on the words64/fill-loop proof
(e64576c) to the words32/copy-loop twin:

- copyWords_spec now carries a frame condition: every byte outside
  `[dst, dst+4n)` is preserved (postcondition + loop invariant), so the theorem
  composes with facts about neighbouring memory (the merge_sort / copy_from_slice
  shape). Discharged per iteration via `Mem.write32_bytes_of_disjoint`.
- Extract `Mem.words32_slotAddr_toNat` (the 4-byte twin of his
  `words64_slotAddr_toNat`) and use it in `words32_write32_outside` / `_snoc`,
  which duplicated the no-wrap slot-address bridge inline.
- CopyWords' `(const 2) shl` step reuses `MemRegion.shl2_eq_mul4` instead of a
  fresh `bv_decide`.

Full `lake build` green (codelib + programs, Lean v4.32.0); copyWords_spec is
axiom-clean (standard axioms + one bv_decide reflection, no sorry).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…use, words64 snoc/extend symmetry

- copyWords_spec: state disjointness as MemRegion.Disjoint (definitional
  restatement; unfolded to the Or once at the top of the proof)
- copyWords_spec: use the Mem.words32_slotAddr_toNat bridge for hda instead
  of re-unfolding MemRegion.slot32; close haddr_d/haddr_s with UInt32.add_comm
  instead of two bv_decide SAT calls
- MemArray: add the general Mem.words64_write64_snoc and re-derive
  words64_write64_extend from it, restoring 32/64 twin symmetry
- docs: qualify the wordsAt references (PR cajal-technologies#106, not yet in-tree) and name
  the intended consumers of slot32_of_shl/slot32_disjoint

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mfornet
mfornet merged commit 46127bc into cajal-technologies:main Jul 16, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants