Feature/u32 to nat - #936
MattCozendey wants to merge 2 commits into
Conversation
The parser reads "3" as U32.from_nat(3n), one App over one Lit node, not a 33-word WCon chain: term_wnf takes U32.from_nat on a nat literal to its word in one step (a proof over 4294967295 stays instant), the pattern reader, the printer and the termination check read the same shape. The compiler keeps a nat literal as a leaf and emits either literal as an immediate (no U32.to_nat over a word past 256, no U32.from_nat call); lit_ctr still folds a match on a literal. Base's Word.zero, Word.inc, U32.inc and U32.from_nat move under def Word(n), before the first bare number. 2000 defs with a wide literal: 1.03 s / 217 MB -> 0.26 s / 81 MB; app_slash_boss_3d check 1.80 s / 420 MB -> 1.43 s / 319 MB. Emitted C over tests and bench/runtime differs only in 0 -> 0ull and one list now in the static image; JS inlines the nat. A literal now infers, so rewrite_non_equation reports the equation miss. Caps: bend.ts 43400, comp.ts 65300. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
lit_ctr takes a U32 literal to its word and a string or nat literal one step: term_descend, the match frame, term_compare and check-lit call it where each stepped a Lit by hand, and comp.ts's fold delegates to it past its NAT_LITERAL_MAX guard. Same tests, same emitted code; bend.ts 43338 ttok, comp.ts 65193. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ea6c0f7 to
23fbb30
Compare
|
Thank you for this. The diagnosis is right and the speedup is real: a U32 literal is 66 constructor nodes today, and parse, lift, check and the report walk each pay per node. One node per literal is the fix. We are not going to take this shape for it. We could either try what #907 and #924 did for Nat and String: make the literal a Note: this reply was written by an AI after it reported the PR to me and I made the decision. If anything here is wrong, reply and I will review it myself. |
|
I'll make a new PR fixing this soon |
A U32 literal is U32.from_nat over a nat literal
Follow-up to #907 and #924, which made a string or nat literal one
Litnode. A bare number now parses as
U32.from_nat(3n): oneAppoverone
Lit, where before the parser built a 33-wordU32{WCon{..}}chainof about 100 nodes per number. The compiler no longer expands a nat past
256 into
U32.to_natover a word either.In bend.ts the parser emits
App(Ref("U32.from_nat"), Lit(n))andu32_litreads that shape back.term_wnftakesU32.from_naton anat literal straight to its word (
u32_word, one shared term pernumber), so a proof over
4294967295costs what it did before. Thatshortcut is a compressed run of the def's own unfolding, and
definitional equality is unchanged.
lit_ctrreads any literal as itsconstructor, a U32 literal as its word and a string or nat literal one
step, and the termination check, the match frame,
term_compareandcheck-lit call it where each used to step a
Litby hand. Patterns andthe printer read the shape too:
case 0:still matches, and an errorspells
3rather thanU32.from_nat(3n).In comp.ts a nat
Litis a leaf.lit_wordgives the immediate foreither literal and feeds
term_constand both emitters, so noU32.from_natcall and noU32.to_natchain reaches the output. Thecompile-time fold still reads a literal scrutinee as its constructor, so
a match on a literal argument folds as it did.
In base.bend,
Word.zero,Word.inc,U32.incandU32.from_natmove under
def Word(n). Forward references are refused andIO.forkwrites
1early in the file. The base case isU32{Word.zero(32n)}.Measured with both trees on tmpfs:
--check-only--check-onlyThe gain scales with how many numbers a program writes. Emitted C over
tests/ and bench/runtime is byte-identical apart from
0spelled0ulland one literal list that now lives in the static image. JS inlines
3000nwhere it hadBigInt(3000). The bench binaries print the sameresults.
Two things a user can observe. A literal now infers its own type, since
it is an application of a typed def, where a bare constructor needed a
goal:
x = 5with no annotation types asU32, andtests/parse/rewrite_non_equationis repinned because the miss is nowthe equation rather than the literal. And a file without
import Basethat writes a bare number reports an undefined
U32.from_natinstead ofan unknown constructor.
New tests:
check/u32_literal_proof(wide and wrapping proofs, literalpatterns),
check/u32_literal_show(a literal prints as its number) andcompile/u32_literal_word(immediates on all three lanes). Locally theinterpret lane over all 1415 tests has the same pass set as
mainplusthose three, and the C and JS lanes were built and run on the 118 tests
whose emitted code changed. The cluster gates were not run from here.
Caps move to 43400 for bend.ts (at 43338) and 65300 for comp.ts (at
65193).
bend.ts is edited, in the parser,
term_wnf, the printer and thepattern reader, since the change lives there by nature. The
term_wnfshortcut is keyed on the def name
U32.from_natin three places: theparser,
u32_litand the guard. A rename in base.bend would fall backto the slow unfolding without an error. bend.lean says literals are base
constructors, not calculus, and may want a line about this reduction.
🤖 Generated with Claude Code