portal benchmark + 3 mismatch defects fixed

Benchmark exercises full save×load matrix across Python/C/asm plus the
mismatch cases (wrong format, truncated input, missing file, corrupt
header). Cases that used to segfault or report wrong paths now degrade
cleanly.

asm (uncommonlisp.s):
- (define var) with no value now binds to VOID instead of segfault
- portal-resume checks sys_read returned full 48-byte header; sanity-
  checks heap_size and heap_base before committing r14/r15 restore.
  Corrupt/truncated portals now return #f cleanly.

py (uncommonlisp.py):
- file-not-found error inside a nested (load) now reports the actual
  missing path (via FileNotFoundError.filename) rather than the outer
  script path.
- _load wraps UnicodeDecodeError (binary file loaded as text) into a
  LispErr with the path; no more raw Python traceback.

tests/portal-benchmark.sh: 50-iter benchmark, 4 parts
  (save / load / cross-process / mismatch-classification).

Representative numbers (this laptop, 2026-04-16):
  setup+save: Python 126ms, C 3ms, asm 0.9ms
  cross-proc: Py→Py 260ms, C→C 6ms, asm→asm 1.5ms
All three test suites still pass: 571 py unit, 131 asm, 189 shared.
This commit is contained in:
russell@unturf.com 2026-04-16 16:57:24 -04:00
parent 57f3c9fab1
commit 2f8b7dc737
5 changed files with 204 additions and 7 deletions

Binary file not shown.

Binary file not shown.

View file

@ -1626,7 +1626,9 @@ env_lookup_both:
cmpq $TAG_PAIR, %rax
je .ev_define_func
# Simple: (define var expr)
# Simple: (define var expr) or (define var) with no expr VAL_VOID
cmpq $VAL_NIL, %r12
je .ev_define_no_expr
movq %r12, %rax
andq $-8, %rax
movq (%rax), %rdi # expr
@ -1634,6 +1636,10 @@ env_lookup_both:
movq %rbp, %rsi
call eval
popq %rbx # var symbol
jmp .ev_define_bind
.ev_define_no_expr:
movq $VAL_VOID, %rax
.ev_define_bind:
movq %rbx, %rdi
movq %rax, %rsi
movq %r14, %rdx
@ -3927,13 +3933,15 @@ bi_portal_resume:
js .pr_fail
movq %rax, %rbx # fd
# Read header
# Read header must get full PORTAL_HDR_SIZE bytes
subq $PORTAL_HDR_SIZE, %rsp
movq $SYS_READ, %rax
movq %rbx, %rdi
movq %rsp, %rsi
movq $PORTAL_HDR_SIZE, %rdx
syscall
cmpq $PORTAL_HDR_SIZE, %rax
jne .pr_bad_magic # short read file is not a portal
# Verify magic
leaq portal_magic(%rip), %rdi
@ -3941,9 +3949,15 @@ bi_portal_resume:
cmpq (%rdi), %rax
jne .pr_bad_magic
# Read heap size, base, r14, r15
# Sanity check header fields before committing
movq 8(%rsp), %rcx # heap_size
testq %rcx, %rcx
jle .pr_bad_magic # zero or negative heap size corrupt
movq 16(%rsp), %rdx # heap_base (must match our mmap)
testq %rdx, %rdx
jz .pr_bad_magic # null heap base corrupt
# Header looks valid commit to r14/r15 restore
movq 24(%rsp), %r14 # restore global env
movq 32(%rsp), %r15 # restore bump pointer