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:
@@ -154,7 +154,7 @@ static const struct row rows[] = {
|
||||
{ "import os;\n"
|
||||
"import rt;\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let p: *void = rt.alloc(4096u64);\n"
|
||||
" let p: *void = rt.malloc(4096u64);\n"
|
||||
" if (p == nil) { return 1; };\n"
|
||||
" let bp: *u8 = p: *u8;\n"
|
||||
" bp[0] = 65u8;\n"
|
||||
@@ -341,7 +341,7 @@ static const struct row rows[] = {
|
||||
* must suppress the builtin and dispatch to the user fn so the
|
||||
* call returns n+100. Pre-gate this site lands in the typed-builtin
|
||||
* path: arg is i64 → returns *i64 → init-type fails against the
|
||||
* declared i64 (and would over-allocate against rt_alloc anyway).
|
||||
* declared i64 (and would over-allocate against rt_malloc anyway).
|
||||
* Inline multi-`package` mirrors driver-concatenated layout; the
|
||||
* `import myos;` directive is silently skipped by locate_import
|
||||
* (no external module by that name). */
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* Pre-fix (#22): wwstage cgalloc's per-field walk routed str-typed
|
||||
* fields through the generic `MOVQ (SP), BX ; MOVQ AX, foff(BX)` path,
|
||||
* which landed only AX (str.ptr) and clobbered BX (str.len) with the
|
||||
* heap base. str.len silently stayed zero (rt_alloc is MAP_ANON-backed,
|
||||
* heap base. str.len silently stayed zero (rt_malloc is MAP_ANON-backed,
|
||||
* so the slot was zero-init rather than garbage — but still wrong).
|
||||
* 990 and 995 byte-identity didn't catch this because nothing in the
|
||||
* bootstrapped selfhost source uses `alloc(T { strfield = "..." })!`.
|
||||
@@ -16,15 +16,15 @@
|
||||
* multi-word fields) is the broader follow-up; this row pins str.
|
||||
*
|
||||
* Pre-fix (#25): wwstage's cgalloc emitted a hardcoded `CALL
|
||||
* rt_alloc(SB)` while cstage routed the same site through
|
||||
* rt_malloc(SB)` while cstage routed the same site through
|
||||
* ffi_resolve("alloc"), so direct `w6c` vs `w6c_ww` on a fixture
|
||||
* without the @symbol decl in scope diverged (cstage: `CALL
|
||||
* alloc(SB)`; wwstage: `CALL rt_alloc(SB)`). The fix swaps both
|
||||
* alloc(SB)`; wwstage: `CALL rt_malloc(SB)`). The fix swaps both
|
||||
* cgenexpr.ww and cgenstmt.ww cgalloc CALL sites to
|
||||
* `ffiresolve(c, "alloc")`, aligning wwstage down to the leaner
|
||||
* `ffiresolve(c, "malloc")`, aligning wwstage down to the leaner
|
||||
* cstage shape (CLAUDE.md rule 10). The `asm_rows` table below pins
|
||||
* the CALL line via single-file `w6c -o` / `w6c_ww -o`; `ww build`
|
||||
* combines lib/os/os.ww into the fixture and would always supply
|
||||
* combines lib/rt/malloc.ww into the fixture and would always supply
|
||||
* the @symbol decl, masking the regression.
|
||||
*
|
||||
* The runtime `rows` below run the generated binary under cstage and
|
||||
@@ -177,20 +177,20 @@ run_driver(const char *driver, const struct row *r, int i)
|
||||
* both stages' .s output. Single-file `w6c` compile (no `ww build`
|
||||
* combine), so the @symbol decl is only in scope when the fixture
|
||||
* writes one — that's how `noscope_*` rows pin the pre-fix wwstage
|
||||
* hardcoded-`rt_alloc` divergence and `withsym_*` rows pin that
|
||||
* hardcoded-`rt_malloc` divergence and `withsym_*` rows pin that
|
||||
* ffiresolve actually hits when the decl is present. */
|
||||
struct asm_row { const char *label; const char *src; const char *want_sym; };
|
||||
|
||||
static const struct asm_row asm_rows[] = {
|
||||
/* Int-field, no @symbol in scope. ffiresolve("alloc") misses the
|
||||
* ffi table and returns "alloc" unchanged on both stages. Pre-fix
|
||||
* wwstage hardcoded `CALL rt_alloc(SB)` → diverged from cstage's
|
||||
* ffi_resolve-mediated `CALL alloc(SB)`. */
|
||||
/* Int-field, no @symbol in scope. ffiresolve("malloc") misses the
|
||||
* ffi table and returns "malloc" unchanged on both stages. Pre-fix
|
||||
* wwstage hardcoded `CALL rt_malloc(SB)` → diverged from cstage's
|
||||
* ffi_resolve-mediated `CALL malloc(SB)`. */
|
||||
{ "noscope_intfield",
|
||||
"package main;\n"
|
||||
"type holder = struct { n: i32 };\n"
|
||||
"fn dummy() *holder = { return alloc(holder { n = 42 })!; };\n",
|
||||
"alloc" },
|
||||
"malloc" },
|
||||
/* Str-field, no @symbol in scope. Confirms the str-field branch
|
||||
* (the #22 fix) still routes its CALL through ffiresolve, not a
|
||||
* stray hardcoded literal copied alongside the #22 emit. */
|
||||
@@ -198,7 +198,7 @@ static const struct asm_row asm_rows[] = {
|
||||
"package main;\n"
|
||||
"type holder = struct { s: str };\n"
|
||||
"fn dummy() *holder = { return alloc(holder { s = \"hi\" })!; };\n",
|
||||
"alloc" },
|
||||
"malloc" },
|
||||
/* Two-step let-then-assign. cglet's CALL site (cgenstmt.ww:647)
|
||||
* is the second cgalloc emit point; this row covers it. */
|
||||
{ "noscope_let_then_assign",
|
||||
@@ -209,16 +209,16 @@ static const struct asm_row asm_rows[] = {
|
||||
" p.n = 7;\n"
|
||||
" return p;\n"
|
||||
"};\n",
|
||||
"alloc" },
|
||||
/* Explicit @symbol decl in scope. ffiresolve("alloc") → "rt_alloc"
|
||||
* so both stages emit `CALL rt_alloc(SB)` — positive confirmation
|
||||
"malloc" },
|
||||
/* Explicit @symbol decl in scope. ffiresolve("malloc") → "rt_malloc"
|
||||
* so both stages emit `CALL rt_malloc(SB)` — positive confirmation
|
||||
* that the ffi table lookup hits, complementing the noscope rows. */
|
||||
{ "withsym_intfield",
|
||||
"package main;\n"
|
||||
"@symbol(\"rt_alloc\") export fn alloc(n: u64) *void;\n"
|
||||
"@symbol(\"rt_malloc\") export fn malloc(n: u64) *void;\n"
|
||||
"type holder = struct { n: i32 };\n"
|
||||
"fn dummy() *holder = { return alloc(holder { n = 42 })!; };\n",
|
||||
"rt_alloc" },
|
||||
"rt_malloc" },
|
||||
};
|
||||
|
||||
/* asm_disp_row — pins the foff=0 displacement formatting (#24 part B).
|
||||
@@ -268,7 +268,7 @@ static const struct asm_disp_row asm_disp_rows[] = {
|
||||
};
|
||||
|
||||
/* asm_call_check — compile via direct w6c / w6c_ww (no `ww build`),
|
||||
* extract the `CALL\t<sym>(SB)` line referencing alloc/rt_alloc from
|
||||
* extract the `CALL\t<sym>(SB)` line referencing alloc/rt_malloc from
|
||||
* each .s file, and verify (a) both stages emit the same line and
|
||||
* (b) the symbol matches r->want_sym. Returns 0 on success. */
|
||||
static int
|
||||
@@ -313,7 +313,7 @@ asm_call_check(const char *bin, const struct asm_row *r, int i)
|
||||
while (fgets(buf, sizeof buf, fc)) {
|
||||
if (strstr(buf, "\tCALL\t")
|
||||
&& (strstr(buf, "alloc(SB)")
|
||||
|| strstr(buf, "rt_alloc(SB)"))) {
|
||||
|| strstr(buf, "rt_malloc(SB)"))) {
|
||||
strncpy(cline, buf, sizeof cline - 1);
|
||||
break;
|
||||
}
|
||||
@@ -321,7 +321,7 @@ asm_call_check(const char *bin, const struct asm_row *r, int i)
|
||||
while (fgets(buf, sizeof buf, fw)) {
|
||||
if (strstr(buf, "\tCALL\t")
|
||||
&& (strstr(buf, "alloc(SB)")
|
||||
|| strstr(buf, "rt_alloc(SB)"))) {
|
||||
|| strstr(buf, "rt_malloc(SB)"))) {
|
||||
strncpy(wline, buf, sizeof wline - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -437,13 +437,13 @@ probe_ww_compile(const char *bin)
|
||||
* pointer-to-struct field). Pre-fix the cgen fell through
|
||||
* silently and returned the inner pointer instead of the
|
||||
* dereferenced field. Surfaced porting w6l/pass.ww. */
|
||||
{ "@symbol(\"rt_alloc\") fn rt_alloc(n: u64) *void;\n"
|
||||
{ "@symbol(\"rt_malloc\") fn rt_malloc(n: u64) *void;\n"
|
||||
"type inner = struct { val: u64 };\n"
|
||||
"type outer = struct { p: *inner };\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let in_: *inner = rt_alloc(8u64): *inner;\n"
|
||||
" let in_: *inner = rt_malloc(8u64): *inner;\n"
|
||||
" in_.val = 42u64;\n"
|
||||
" let o: *outer = rt_alloc(8u64): *outer;\n"
|
||||
" let o: *outer = rt_malloc(8u64): *outer;\n"
|
||||
" o.p = in_;\n"
|
||||
" return o.p.val: i32;\n"
|
||||
"};", 42 },
|
||||
|
||||
@@ -108,7 +108,7 @@ main(void)
|
||||
} cases[] = {
|
||||
/* Real bootstrap-style links: every selfhost main.o paired
|
||||
* with libwwrt.a. Exercises both the .o code path and the
|
||||
* archive two-pass loader (rt_alloc / rt_syscall / rt_abort
|
||||
* archive two-pass loader (rt_malloc / rt_syscall / rt_abort
|
||||
* are pulled from libwwrt.a as undefs are encountered). */
|
||||
{ "wwdump+libwwrt",
|
||||
"%s/selfhost/cmd/wwdump/main.o %s/out/lib/libwwrt.a" },
|
||||
|
||||
Reference in New Issue
Block a user