Skip to content

feat: shen.x.sha256 host (OpenSSL) - #48

Open
pyrex41 wants to merge 7 commits into
mainfrom
feat/shen-x-sha256
Open

feat: shen.x.sha256 host (OpenSSL)#48
pyrex41 wants to merge 7 commits into
mainfrom
feat/shen-x-sha256

Conversation

@pyrex41

@pyrex41 pyrex41 commented Jul 27, 2026

Copy link
Copy Markdown
Owner

Summary

  • OpenSSL SHA256 via LuaJIT FFI for shen-extensions
  • SHEN_X_SHA256=pure disables

Test plan

  • Bifrost agreement with go/rust on sha256 vectors

Reuben Brooks and others added 7 commits July 27, 2026 15:18
… live lambdas

On the S41.2 kernel every (define ...) in a loaded file runs
shen.update-lambdatable, which (set)s shen.*lambdatable* to an assoc
list whose entries hold live curried lambda functions. The fasl
recorder captured that as an ordinary "g" (set) record, kdata_ser hit
the closure ("unserializable KDATA value: function"), and the whole
file became fasl-uncacheable — in practice ~every stdlib and user file,
so the ~1s stdlib load re-compiled on every boot and every (load) paid
full reader+macro+typecheck cost on every run.

Record a new "lt" record instead: diff the new table against the
current global (the recorder runs before the original set, so the old
table is still live) and store only the NAMES whose entry was
added/replaced. On replay, rebuild each entry from the live defun the
same way shen.update-lambdatable does — shen.lambda-entry (which reads
the arity property, already replayed by the preceding "p" record in
stream order) + shen.assoc->. Mirrors the existing "lf" special case
for the older kernel's shen.lambda-form put path.

Bump FASL_FORMAT to SHENFASL5 so stale caches miss cleanly.

Measured (arm64 macOS, LuaJIT 2.1 rolling): trivial-script startup
1.5s -> 0.34s warm; all 21 StLib files now fasl-hit (previously 19/21
uncacheable). make test 500/0, run-kernel-tests 134/0, warm/cold suite
stdout identical modulo the by-design 'run time' banner drop.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… user output

-q sets *hush*, which on the 41.2 kernel gates pr itself, so a suite run
under -q prints NOTHING including the tests' own (output ...) lines
(issue #46 item 3) — unusable for golden-output batch runners. Meanwhile
default mode echoes every loaded form's value/type (~100 lines on urdr
suites) plus nondeterministic 'run time: N secs' banners.

--hush-load (bin/shen flag, or env SHEN_HUSH_LOAD=1) drops ONLY what
load itself prints to standard output: the per-form value/type echo
(shen.eval-and-print / shen.work-through) and the run-time/typechecked
banners. User (output ...)/pr from loaded forms is untouched, as are
file streams and stderr.

Mechanism reuses the distinction the fasl 'e' capture already relies
on: load's chatter is pr'd OUTSIDE any eval-kl chunk, at the chunk
nesting depth load was entered at, while user output runs INSIDE a
chunk. boot.lua wraps load to publish that depth (P.LOADPR_DEPTH,
saved/restored so nested loads compose); prims' native pr drops stdout
writes at exactly that depth; prims.compile_and_load maintains the
depth counter (only when recording or the flag is on — the default
path stays a bare tail call). Fasl integration: the load wrapper sits
inside install_fasl's wrappers, so a miss recorded under --hush-load
still captures the full 'e' echo bytes (replayable to a later
non-hushed run); a replay hit is gated at the 'e' re-emit site.

Verified: default-mode stdout byte-identical (warm hit) / identical mod
timing lines (cold) on urdr prng+world suites; hush warm==cold==env
form; every hush-mode line present in shen-cl's output for the same
suite; cache recorded under hush replays full echo non-hushed; -q
still fully silent; make test 500/0; run-kernel-tests 134/0 ok.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…egen

jit.p profiling of urdr's software-SHA-256 suite (shen/tests/prng,
LuaJIT 2.1 rolling, arm64) showed 27% of time in the interpreter with
cons/append/list.take at the top, and jit.v showed why: 282 of the
~350 aborted trace attempts were 'blacklisted at prims.lua:239' — the
tiny F["cons"] wrapper proto. cons is hot enough that traces root in
it, those aborts blacklist the proto, and from then on EVERY caller
trace that reaches a cons call aborts, demoting the whole workload to
the interpreter.

Emit the construction inline instead — ENV.CMT is the runtime Cons
metatable — so there is no proto to blacklist and hot traces can sink
the allocation. Same late-binding tradeoff as the existing ARITH2
fast paths (a runtime redefinition of cons is not seen by compiled
exact-arity call sites).

urdr prng suite wall (warm fasl, best of 5): 3.20s -> 1.43s (-55%).
world suite: 4.4s -> 3.08s. After: 0 blacklisted traces, interpreter
share 27% -> 8%, compiled share 37% -> 57%.

Verified: suite stdout byte-identical (warm and cold); world golden
exact; make test 500/0; run-kernel-tests 134/0 ok.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
After the cons-inline fix, jit.p on urdr's prng suite still shows ~27%
of wall time in the GC — the SHA-256/bigint workload is cons churn the
port cannot avoid (bits are Shen lists). Interleaved A/B on CPU time:

  pause=400          -15% prng, -20% world, peak RSS 30MB -> 60MB
  pause=400 mul=100  -16%                   (stepmul adds ~nothing)
  pause=800 mul=100  -25-28%                peak RSS 108MB
  pause=200 mul=100  no change (stepmul alone is not the lever)

Ship pause=400 as the boot default: solid suite-level win for a
bounded 2x peak-RSS cost. SHEN_GC=off preserves the host's defaults
(embedders managing their own GC); SHEN_GC="pause[,stepmul]" sets
explicit values (800,100 documented for batch runs).

Also rejected: jit.opt loopunroll=60/callunroll=10 — helps only warm
in-process re-runs, cold-process suite wall got 30% WORSE (trace
compile churn); left at current defaults.

Verified: prng warm stdout byte-identical; world golden exact;
make test 500/0; run-kernel-tests 134/0 ok.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…unction

The 41.2 shen->kl translator compiles every call to a function whose
arity is unknown at translation time (chiefly forward references
within a file) as ((fn name) args...). Kernel fn (reader.kl) then pays
an arity property get PLUS an assoc over the whole shen.*lambdatable*
per call, and returns the table's curried lambda, which APP applies
one MKFUN closure at a time. jit.p F3 stacks on urdr's prng suite put
~35% of samples under fn (is_cons/equal < assoc < fn <
urdr.prng.word.add-all/add2/...).

Native fn_fast (prims.install_native_stdlib): for a symbol naming a
live F entry with known positive arity, verify the lambdatable entry
ONCE per (name, lambdatable identity) through the original fn — a
miss still raises the kernel's "fn: X is undefined" — then return the
RAW F[name] function. APP dispatches a raw function with recorded
arity identically to the curried chain (exact, partial, and
over-application) without the per-argument closure allocations. Every
(set shen.*lambdatable* ...) builds a fresh spine, so table identity
invalidates the verification cache; arity-0 (kernel fn CALLS those)
and unknown-arity names always take the original path.

Interleaved A/B (user+sys CPU, 6 runs each, sorted):
  prng:  3.14 3.34 3.43 3.61 3.68 3.88  ->  2.32 2.47 2.47 2.51 2.54 3.38  (-27%)
  world: 4.59 4.70 4.82 5.01 5.09 5.39  ->  3.92 4.16 4.35 4.54 4.61 5.35  (-12%)

Verified: prng warm stdout byte-identical; world golden exact;
make test 500/0; run-kernel-tests 134/0 ok.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
jit.p Fl on urdr's prng suite put ~10% of leaf samples in the kernel
append (sys.kl): non-tail-recursive, one F["hd"]/F["tl"]/F["append"]
lookup per element of the first list. word.rotr / word.bits concatenate
32-bit bit-lists through append on every SHA round.

Native install_native_stdlib override: walk A once building a fresh
spine iteratively (exactly |A| new cons cells — same allocation as the
recursive form), splice B onto the last cell. Fresh cells are unshared
until return, so in-place tail assignment is unobservable. Improper /
non-list inputs delegate to the original for a byte-identical
simple-error.

Serial warm A/B (user+sys CPU, dual SHEN_FASL_DIR caches, N=8–10):
  prng:  med 0.93 -> 0.91 (-2%), mean 1.00 -> 0.92 (-8%);
         final recheck med 0.90 -> 0.87 (-3%), mean 0.95 -> 0.91 (-4%)
  world: med 2.28 -> 1.85 (-19%)

Also tried (not shipped): pure_tail_self bare f-error relaxation —
correctly emits LOOP for multi-clause defines (list.drop/nth) but mean
prng regressed ~+14% on short-list SHA walks; left documented in
doc/PERF-URDR-RESULTS.md.

Verified: prng/world warm stdout golden-identical; make test 500/0;
run-kernel-tests 134/0 ok.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Binds shen.x.sha256-octets-host from install_native_stdlib when libcrypto
loads. Disable with SHEN_X_SHA256=pure. See pyrex41/shen-extensions.
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.

1 participant