lib/rt: rename rt_alloc → rt_malloc; rt.alloc → rt.malloc
Hare's canonical runtime allocator is rt::malloc with linker symbol
rt.malloc (ref/hare/rt/malloc.ha:27,78). ww kept the dot→underscore
Plan 9 convention (CLAUDE.md rule 4) so the linker symbol becomes
rt_malloc; the lib/rt exported function name becomes malloc; ww
callers say rt.malloc(...).
The language builtin keyword stays `alloc(T)!` — unchanged from Hare
(ref/hare/hare/lex/token.ha:21 ltok::ALLOC, parse/expr.ha:398
builtin()). The rename only touches the lowered linker symbol and the
exported function name behind it; the user-facing syntax for
heap-allocation is identical to Hare.
Surface:
- rt/alloc.s: TEXT rt_alloc → TEXT rt_malloc, labels updated
- lib/rt/malloc.ww: @symbol("rt_malloc") fn malloc(...) (was rt_alloc/alloc)
- rt/ensure.ww: local FFI decl + call site updated to malloc; `!` dropped
on the direct FFI call (rt_malloc returns *void, not a tagged union)
- 18 .ww callers: rt.alloc(...) → rt.malloc(...)
- cstage cmd/wcc/check.c + wwstage selfhost/cmd/wcc/check.ww
alloc-builtin suppression gate routes through ffi_resolve("malloc")
for the lowering; the user-shadow check still keys on the BUILTIN
KEYWORD "alloc" since that is what `alloc(...)` parses as. Adding
"malloc" to the user-shadow check was unnecessary and was reverted
during pre-commit review.
- cstage cmd/w6c/cgen.c: 2× ffi_resolve("alloc") → ffi_resolve("malloc")
- wwstage cgenexpr/cgenstmt: 2× ffiresolve(c, "alloc") → ffiresolve(c, "malloc")
- Test fixtures (700_e2e, 758_cgalloc_str_field, 990_selfhost, 992_w6l_ww,
selfhost/test/tagged_ptr_ret.ww): updated inline ww sources to the new
decl + call form
This is commit 2 of 3 in the lib/rt extraction (#38). Commit 3 closes
the OOM contract — return type becomes nullable *void and the builtin
lowering null-checks + propagates nomem.
Verified 132/132 + 995_self_rebuild byte-identity (5 wwstage tools
round-trip identical) + make clean cold rebuild.
This commit is contained in:
@@ -438,7 +438,7 @@ export fn getdents64(fd: i32, buf: *u8, n: u64) i64 = {
|
||||
|
||||
// rt_envp — runtime-side getter. rt/start.s captures envp into a DATAW
|
||||
// slot before calling main; this binding lifts the captured pointer
|
||||
// into ww. Same FFI shape as rt_syscall / rt_alloc / rt_abort: a TEXT
|
||||
// into ww. Same FFI shape as rt_syscall / rt_malloc / rt_abort: a TEXT
|
||||
// symbol the linker resolves. The returned `**u8` is a NUL-terminated
|
||||
// table of `*u8` entries, each pointing at a NUL-terminated
|
||||
// "NAME=VALUE" byte sequence.
|
||||
@@ -1709,7 +1709,7 @@ export fn position(d: *decoder) i32 = {
|
||||
|
||||
package rt;
|
||||
|
||||
// alloc — mmap-backed page allocator. Untyped: `alloc(n)` returns a
|
||||
// malloc — mmap-backed page allocator. Untyped: `malloc(n)` returns a
|
||||
// `*void`; callers cast to the target type. Diverges from Hare: Hare
|
||||
// exposes `alloc` / `free` as typed language builtins that the
|
||||
// compiler lowers to rt::malloc/rt::free; ww has no such builtins,
|
||||
@@ -1717,15 +1717,15 @@ package rt;
|
||||
// need a typed allocation pattern wrap this with a cast plus a stored
|
||||
// capacity (see [[strings.dup]], [[memio.dynamic]]).
|
||||
//
|
||||
// OOM: rt_alloc is a bare mmap(MAP_ANON|MAP_PRIVATE) wrapper with no
|
||||
// OOM: rt_malloc is a bare mmap(MAP_ANON|MAP_PRIVATE) wrapper with no
|
||||
// error path. The raw Linux mmap syscall returns a negative errno cast
|
||||
// to `*void` on failure (e.g. `(void*)-12` for ENOMEM); the
|
||||
// `MAP_FAILED` (`(void*)-1`) value is a libc-wrapper convention that
|
||||
// rt_alloc doesn't apply. Neither `== nil` nor `== (void*)-1` catches
|
||||
// rt_malloc doesn't apply. Neither `== nil` nor `== (void*)-1` catches
|
||||
// it; any deref of such a return faults. Today the stdlib does not
|
||||
// check; OOM faults on first dereference. A typed fallible variant is
|
||||
// a future task (task #39). ref/hare/rt/malloc.ha:27.
|
||||
@symbol("rt_alloc") export fn alloc(n: u64) *void;
|
||||
@symbol("rt_malloc") export fn malloc(n: u64) *void;
|
||||
|
||||
// strings — operations over str ({ptr,len}). Hare port; see
|
||||
// ref/hare/strings/.
|
||||
@@ -1828,7 +1828,7 @@ export fn dup(s: str) str = {
|
||||
r.ptr = nil;
|
||||
r.len = 0;
|
||||
if (s.len == 0) { return r; };
|
||||
let buf: *u8 = rt.alloc(s.len: u64): *u8;
|
||||
let buf: *u8 = rt.malloc(s.len: u64): *u8;
|
||||
let i: i32 = 0;
|
||||
for (i < s.len) { buf[i] = s[i]; i += 1; };
|
||||
r.ptr = buf;
|
||||
@@ -1850,7 +1850,7 @@ export fn dup(s: str) str = {
|
||||
// (#46). The pre-allocated slice has `cap == s.len`, so appendstr's
|
||||
// rt_ensure call never reaches the grow branch.
|
||||
//
|
||||
// Empty input bypasses the alloc: rt_alloc(0) is an mmap of 0 bytes
|
||||
// Empty input bypasses the alloc: rt_malloc(0) is an mmap of 0 bytes
|
||||
// which returns -EINVAL, and the alloc-slice `?` shortcut routes
|
||||
// that through nomem — Hare's heap allocator hands back a sentinel
|
||||
// instead (#47). Return `{nil, 0, 0}` directly so callers get the
|
||||
@@ -1906,7 +1906,7 @@ export fn concat(strs: str...) str = {
|
||||
r.ptr = nil;
|
||||
r.len = 0;
|
||||
if (total == 0) { return r; };
|
||||
let buf: *u8 = rt.alloc(total: u64): *u8;
|
||||
let buf: *u8 = rt.malloc(total: u64): *u8;
|
||||
let off: i32 = 0;
|
||||
i = 0;
|
||||
for (i < strs.len) {
|
||||
@@ -1939,7 +1939,7 @@ export fn join(delim: str, strs: str...) str = {
|
||||
r.ptr = nil;
|
||||
r.len = 0;
|
||||
if (total == 0) { return r; };
|
||||
let buf: *u8 = rt.alloc(total: u64): *u8;
|
||||
let buf: *u8 = rt.malloc(total: u64): *u8;
|
||||
let off: i32 = 0;
|
||||
i = 0;
|
||||
for (i < strs.len) {
|
||||
@@ -2616,7 +2616,7 @@ export fn lpad(s: str, p: rune, maxlen: i32) str = {
|
||||
if (s.len >= maxlen) { return dup(s); };
|
||||
let scratch: [4]u8;
|
||||
let pad: []u8 = runebytes(scratch[0:4], p);
|
||||
let buf: *u8 = rt.alloc(maxlen: u64): *u8;
|
||||
let buf: *u8 = rt.malloc(maxlen: u64): *u8;
|
||||
let padwrite: i32 = (maxlen - s.len) * pad.len;
|
||||
if (padwrite > maxlen) { padwrite = maxlen; };
|
||||
let off: i32 = 0;
|
||||
@@ -2699,7 +2699,7 @@ export fn rpad(s: str, p: rune, maxlen: i32) str = {
|
||||
if (s.len >= maxlen) { return dup(s); };
|
||||
let scratch: [4]u8;
|
||||
let pad: []u8 = runebytes(scratch[0:4], p);
|
||||
let buf: *u8 = rt.alloc(maxlen: u64): *u8;
|
||||
let buf: *u8 = rt.malloc(maxlen: u64): *u8;
|
||||
let k: i32 = 0;
|
||||
for (k < s.len) {
|
||||
buf[k] = s[k];
|
||||
|
||||
@@ -44,12 +44,12 @@ fn allocbox() (*point | nomem) = {
|
||||
};
|
||||
|
||||
// Task #32: slice-form `let s: []T = alloc([], n)!;` shortcut. Both
|
||||
// stages must lower to `n*esz` bytes via rt_alloc, abort on null, and
|
||||
// stages must lower to `n*esz` bytes via rt_malloc, abort on null, and
|
||||
// build a {ptr, 0, n} header in the let slot. Pre-#32 wwstage fell
|
||||
// through to cgalloc, allocating 8B and dropping the slice header
|
||||
// entirely — silent miscompile. Cap-only would pass on a junk header
|
||||
// pointing to dead memory; write-then-read on s[0]/s[cap-1] proves
|
||||
// the ptr field is a real rt_alloc'd region (would SIGSEGV otherwise).
|
||||
// the ptr field is a real rt_malloc'd region (would SIGSEGV otherwise).
|
||||
// IMULQ esz path is currently unreachable from user code — check.c
|
||||
// pins the alloc shape to []u8 (cstage check.c:1052-1082) — so this
|
||||
// row only exercises esz=1; the cgen elemsizeofc resolution stays
|
||||
|
||||
Reference in New Issue
Block a user