Phase 7k: bytes(n) / words(n) allocators (retire mem_alloc)

Allocation reads as intent, not malloc:

  mem_alloc(64)          -> bytes(64)              (64 bytes -> a byte buffer)
  mem_alloc(w * h * 4)   -> words(w * h)           (w*h 32-bit words)

bytes(n) mallocs n bytes and returns a plain pointer (byte-indexed); words(n)
mallocs n*4 bytes and returns a `words` pointer (int-indexed). Since words(X) and
mem_alloc(X*4) allocate the identical number of bytes, the migration cannot change
any allocation size — the `* 4` factor just moves from the argument into the
allocator name, pairing naturally with the Phase-7j `words` retyping
(`var fb: words = words(w * h)`).

Migrated 102 sites (mem_alloc(E*4) -> words(E), else bytes(E)); deleted the
mem_alloc intrinsic. mem_realloc/free/copy/set stay as the low-level
reallocation/free family. Reseeded (22565 lines); C-free fixpoint holds; goldens
byte-identical; 18/18; vocab + doc-fences clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-08-28 02:26:15 +03:00
parent 5cab68ea2e
commit 86948d2b19
22 changed files with 3118 additions and 3069 deletions

View file

@ -7,7 +7,7 @@ property Buf { data: ptr = null, len: int = 0, cap: int = 0 }
fn buf_new() -> Buf {
let b = new Buf
b.cap = 256
b.data = mem_alloc(b.cap)
b.data = bytes(b.cap)
b.len = 0
return b
}

View file

@ -138,6 +138,17 @@ fn emit_call(e: Node) -> Val {
else { emit(" call i32 (ptr, ...) @printf(ptr @.fmt_int, i32 " + `{a.code})\n`) }
return val("0", "void")
}
if (name == "bytes") { # bytes(n): allocate n bytes -> a byte buffer
let n = emit_expr(e.kids[0])
let w = emit_bind(`zext i32 {n.code} to i64`)
return val(emit_bind(`call ptr @malloc(i64 {w})`), "ptr")
}
if (name == "words") { # words(n): allocate n 32-bit words
let n = emit_expr(e.kids[0])
let by = emit_bind(`mul i32 {n.code}, 4`)
let w = emit_bind(`zext i32 {by} to i64`)
return val(emit_bind(`call ptr @malloc(i64 {w})`), "words")
}
if (name == "fx") { let a = emit_expr(e.kids[0]); return val(emit_bind(`shl i32 {a.code}, 16`), "fixed") }
if (name == "flr") { let a = emit_expr(e.kids[0]); return val(emit_bind(`ashr i32 {a.code}, 16`), "int") }
if is_intrinsic(name) { return emit_intrinsic(name, e) }

View file

@ -7,7 +7,7 @@ var g_intrin_ok: bool = false
# is `name` a low-level intrinsic? A pure name check, so it can gate dispatch
# without evaluating arguments (which could clobber shared state).
fn is_intrinsic(name: ptr) -> bool {
if (name == "mem_alloc") or (name == "mem_realloc") { return true }
if (name == "mem_realloc") { return true }
if (name == "file_open") or (name == "file_read") or (name == "file_write") { return true }
if (name == "file_seek") or (name == "file_tell") or (name == "file_close") { return true }
if (name == "os_argc") or (name == "os_arg") or (name == "os_exit") or (name == "file_stderr") { return true }
@ -23,11 +23,7 @@ fn arg_code(e: Node, i: int) -> ptr { let v = emit_expr(e.kids[i]); return v.co
fn emit_intrinsic(name: ptr, e: Node) -> Val {
g_intrin_ok = true
# ptr_null / ptr_is_null are the `null` literal and `x == null` now.
if (name == "mem_alloc") {
let n = arg_code(e, 0)
let w = emit_bind(`zext i32 {n} to i64`)
return val(emit_bind(`call ptr @malloc(i64 {w})`), "ptr")
}
# mem_alloc is bytes(n) / words(n) now (see emit_call).
if (name == "mem_realloc") {
let p = arg_code(e, 0); let n = arg_code(e, 1)
let w = emit_bind(`zext i32 {n} to i64`)

View file

@ -7,7 +7,7 @@ fn read_file(path: str) -> ptr {
file_seek(f, 0, 2)
let n = file_tell(f)
file_seek(f, 0, 0)
let buf = mem_alloc(n + 1)
let buf = bytes(n + 1)
file_read(f, buf, n)
buf[n] = 0
file_close(f)

View file

@ -53,7 +53,7 @@ fn lex(src: ptr) -> void {
if c == 34 { # "string"
i = i + 1
let start = i
let out = mem_alloc(n)
let out = bytes(n)
var j = 0
while i < n and src[i] != 34 {
if src[i] == 92 { # backslash escape
@ -72,7 +72,7 @@ fn lex(src: ptr) -> void {
}
if c == 96 { # `interpolated string` — captured raw
i = i + 1
let out = mem_alloc(n)
let out = bytes(n)
var j = 0
while i < n and src[i] != 96 { out[j] = src[i]; j = j + 1; i = i + 1 }
i = i + 1

File diff suppressed because it is too large Load diff

View file

@ -35,7 +35,7 @@ fn ptype() -> ptr {
pi = pi + 1
eat_op("]")
let el = ptype()
let out = mem_alloc(len(el) + 3)
let out = bytes(len(el) + 3)
out[0] = 91; out[1] = 93 # "[]"
var i = 0
while el[i] != 0 { out[2 + i] = el[i]; i = i + 1 }
@ -75,7 +75,7 @@ fn parse_hole(inner: ptr) -> Node { # re-lex+parse an embedded exp
fn parse_interp(raw: ptr) -> Node {
let n = len(raw)
var acc: Node = null
let lit = mem_alloc(n + 1)
let lit = bytes(n + 1)
var lj = 0
var i = 0
while i < n {

View file

@ -14,16 +14,16 @@ fn char_is_alnum(c: int) -> bool { return char_is_alpha(c) or char_is_digit(c) }
# integer -> fresh decimal string
fn itoa(v: int) -> ptr {
if v == 0 { let z = mem_alloc(2); z[0] = 48; z[1] = 0; return z }
if v == 0 { let z = bytes(2); z[0] = 48; z[1] = 0; return z }
var neg = false
var x = v
if x < 0 { neg = true; x = 0 - x }
let tmp = mem_alloc(16)
let tmp = bytes(16)
var n = 0
while x > 0 { tmp[n] = 48 + x % 10; x = x / 10; n = n + 1 }
var total = n
if neg { total = total + 1 }
let out = mem_alloc(total + 1)
let out = bytes(total + 1)
var k = 0
if neg { out[0] = 45; k = 1 }
var i = 0

View file

@ -9,7 +9,7 @@ program T {
print(classify(90)) # 9
print((1 << 4)) # 16
print(((4 | 1) & 6)) # 4
let p = mem_alloc(16)
let p = bytes(16)
p[1] = 9999
print(p[1]) # 9999
}