From da1063f1bb472a97713f1c6f9d72327fe58139ed Mon Sep 17 00:00:00 2001 From: agra Date: Mon, 25 May 2026 15:33:28 +0300 Subject: [PATCH] mem: allocator `init` returns state by value (drops state-struct heap alloc) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Building on the Option 3 lvalue-borrow rule, the long-lived allocators in `library/modules/allocators.sx` (GPA, Arena, TrackingAllocator) now return their state by value instead of via a heap-allocated `*T`. The caller binds the result to a local; the local IS the allocator state. `xx local` borrows that storage under Option 3, so the `Allocator` protocol value's `ctx` points at the local — no heap allocation for the state struct, no `free` of the state needed. ```sx gpa := GPA.init(); // GPA (value) arena := Arena.init(xx gpa, 4096); // Arena (value) tracker := TrackingAllocator.init(xx gpa); // TrackingAllocator (value) push Context.{ allocator = xx tracker, data = null } { ... } ``` Why by-value: - One fewer `libc_malloc` per allocator instance. - No state-struct leak. The local is reclaimed at scope exit; `deinit` only handles downstream resources (chunks, etc.) — not its own struct. - Owning structs can embed allocators as value fields directly. Callsite changes: - `library/modules/ui/pipeline.sx`: `arena_a: Arena;` / `arena_b: Arena;` (was `*Arena;`). The `build_arena: *Arena` local takes `@self.arena_a` / `@self.arena_b`. - `examples/126-xx-recover-then-dispatch.sx`: `recovered == @gpa` instead of `recovered == gpa` (gpa is a value now). - `examples/135-xx-lvalue-borrows.sx`: drop the `tracker_ptr.*` deref — `init` already returns the value. - `examples/50-smoke.sx`: Arena alloc counts dropped by 1 (no state-struct allocation). Comments + snapshot updated. `Arena.deinit` drops the trailing `parent.dealloc(xx a)` — the caller's local owns the storage. FFI IR snapshots regenerated to reflect the new signatures: `@GPA.init` returns `i64` (was `ptr`); `@Arena.init` and `@TrackingAllocator.init` use sret returns (was `ptr`). CLAUDE.md "Allocator construction" rule rewritten around the by-value convention. The forbidden caller-provides-storage and redundant-pointer-rename patterns are still forbidden but for the right reasons now (verbose, fragile) rather than as a workaround for the old `init() -> *T` shape. 157/157 example tests pass; chess clean on macOS, iOS sim, and Android via `tools/verify-step.sh`. --- CLAUDE.md | 109 +++++++++++------- current/CHECKPOINT-MEM.md | 40 ++++++- examples/126-xx-recover-then-dispatch.sx | 4 +- examples/135-xx-lvalue-borrows.sx | 3 +- examples/50-smoke.sx | 16 +-- library/modules/allocators.sx | 51 ++++---- library/modules/ui/pipeline.sx | 15 ++- tests/expected/50-smoke.txt | 6 +- .../ffi-jni-call-03-methodid-sharing.ir | 8 +- tests/expected/ffi-jni-call-04-jint-return.ir | 8 +- .../expected/ffi-jni-call-05-jlong-return.ir | 8 +- .../ffi-jni-call-06-jdouble-return.ir | 8 +- .../ffi-jni-call-07-jboolean-return.ir | 8 +- .../ffi-jni-call-08-jobject-return.ir | 8 +- tests/expected/ffi-jni-call-09-static.ir | 8 +- tests/expected/ffi-jni-class-08-call.ir | 8 +- .../expected/ffi-jni-env-02-lexical-direct.ir | 8 +- .../ffi-objc-call-03-selector-sharing.ir | 8 +- .../expected/ffi-objc-call-06-sret-return.ir | 8 +- 19 files changed, 191 insertions(+), 141 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index dc7f0d2..03c8b00 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -164,59 +164,84 @@ beats `else => unreachable` beats `else => /* hope */`. ### Allocator construction -❌ **Forbidden:** the "caller provides storage" pattern (in any form): +✅ **Required shape:** `init` returns the concrete state **by value**. +The caller binds it to a local (or embeds it in a struct field); that +local IS the allocator's storage. `xx local` borrows the local's +address into the `Allocator` protocol value — no heap allocation for +the state struct, no `free` of the state needed, no caller-provides- +storage ceremony at the call site. ```sx -// NEVER write this — explicit @ptr: -g_gpa : GPA = ---; -alloc := GPA.create(@g_gpa); - -// NEVER write this — UFCS-disguised same pattern: -gpa_state : GPA = .{ alloc_count = 0 }; -gpa := gpa_state.create(); - -// NEVER write this — in-place init on a struct field: -self.arena_a.create(parent, size); -``` - -❌ **Also forbidden:** wrapping an `init` result through a cast just -to bind a typed pointer you already have: - -```sx -// NEVER write this — tracker is already *TrackingAllocator: -tracker := TrackingAllocator.init(context.allocator); -t : *TrackingAllocator = xx tracker; // redundant rename -t.report(); -``` - -✅ **Required:** `init` returns the concrete typed pointer (`*T`); -caller casts `xx ptr` to `Allocator` only at use sites that need the -protocol value. - -```sx -gpa := GPA.init(); // *GPA -arena := Arena.init(xx gpa, 4096); // *Arena ; xx gpa → Allocator for parent -tracker := TrackingAllocator.init(context.allocator); // *TrackingAllocator +gpa := GPA.init(); // GPA (value, stack-local) +arena := Arena.init(xx gpa, 4096); // Arena (value) +tracker := TrackingAllocator.init(xx gpa); // TrackingAllocator (value) push Context.{ allocator = xx tracker, data = null } { ... } print("gpa allocs: {}\n", gpa.alloc_count); // direct field access tracker.report(); // direct method call arena.reset(); // direct method call +arena.deinit(); // frees the chunks; the + // Arena struct itself + // goes away with the local ``` -The rule exists because: -- `create` returning `Allocator` forces an `instance()` accessor or a - cast-back to recover the typed pointer (extra step every time). -- Caller-storage patterns are verbose, error-prone (easy to pass the - wrong @ptr), and an artifact of an earlier allocator design. -- `init` returning `*T` matches Zig conventions and lets the caller - decide where/how to cast to `Allocator`. +Why by-value: -See `current/CHECKPOINT-MEM.md` ISSUE-MEM-005 for the migration -history. If an existing allocator type still uses the old `create` -pattern, migrate it OR ask the user — never propagate the pattern -in new code, docstrings, examples, or tests. +- No state-struct leak. The local is reclaimed when its scope ends; no + explicit `deinit` is needed to free the struct (chunks/buffers the + allocator manages downstream still need cleanup — that's orthogonal). +- One fewer `libc_malloc` per allocator instance. +- Composition stays clean: a struct that owns an allocator embeds it + directly (`arena_a: Arena;`) rather than holding a pointer (`arena_a: + *Arena;`). The owning struct's heap-alloc covers it. +- `xx local` is borrow-mode under sx's protocol-erasure rule (see + `specs.md §3` — Ownership and Lifetime). Mutations through the + protocol are visible to the local. + +❌ **Forbidden:** the *manual* "caller provides storage" pattern, +because it pushes raw-struct construction at the user. This is a +different shape from the value-return rule above — the user writes +out the type, declares uninitialised state, and invokes a separate +`create`/`init_in_place` that mutates it. Verbose, fragile, easy to +forget the init step: + +```sx +// NEVER write this — explicit @ptr: +g_gpa : GPA = ---; +GPA.create(@g_gpa); + +// NEVER write this — UFCS-disguised same pattern: +gpa_state : GPA = .{ alloc_count = 0 }; +gpa_state.create(); + +// NEVER write this — in-place init on a struct field: +self.arena_a.create(parent, size); +``` + +The value-return pattern subsumes these use cases without the +gotcha: `gpa := GPA.init();` already gives the caller a local; if +they want the storage in a struct field, `self.arena_a = +Arena.init(parent, size);` works directly. + +❌ **Also forbidden:** wrapping an `init` result through a cast just +to bind a "typed pointer" you don't actually need (it's a value now): + +```sx +// NEVER write this — tracker is already a TrackingAllocator value: +tracker := TrackingAllocator.init(xx gpa); +t : *TrackingAllocator = xx @tracker; // redundant rename +t.report(); +``` + +Call methods directly on the local — `tracker.report();` works via +UFCS auto-address-of, no manual pointer juggling required. + +When migrating an existing allocator from the old `init() -> *T` +shape to the new `init() -> T`, also drop the trailing +`parent.dealloc(xx a)` from any `deinit` — the caller's local owns +the storage now, deinit only frees downstream resources (chunks, +counters' backing, etc.). ### Long-lived containers growing through `context.allocator` diff --git a/current/CHECKPOINT-MEM.md b/current/CHECKPOINT-MEM.md index 2aee981..6997ccb 100644 --- a/current/CHECKPOINT-MEM.md +++ b/current/CHECKPOINT-MEM.md @@ -5,6 +5,35 @@ Tracking checkpoint for the mem.sx Zig-aligned implementation ## Last completed step +- **Allocator `init` returns the state by value.** Building on the + Option 3 lvalue-borrow rule, `GPA.init`, `Arena.init`, and + `TrackingAllocator.init` now return `T` (not `*T`). The caller binds + the local; the local IS the storage. `xx local` borrows under Option + 3 so the `Allocator` protocol value's `ctx` points at the local. + Saves one `libc_malloc` per allocator instance; closes the + state-struct leak surface (the local goes away with its scope, no + explicit `deinit` needed for the struct itself). + + Migration: + - `library/modules/allocators.sx`: `init` signatures changed + (`-> T` instead of `-> *T`); `Arena.deinit` drops the trailing + `parent.dealloc(xx a)` — caller owns the storage. + - `library/modules/ui/pipeline.sx`: `arena_a: Arena;` / + `arena_b: Arena;` (was `*Arena;`); `@self.arena_a` / + `@self.arena_b` at the use site that needs `*Arena` for the + `build_arena` local. + - `examples/126-xx-recover-then-dispatch.sx` updated: comparisons + against `*GPA` use `@gpa` now. + - `examples/135-xx-lvalue-borrows.sx` simplified: no + `tracker_ptr.*` deref needed. + - `examples/50-smoke.sx` Arena counts dropped by 1 each (no + state-struct alloc); comments + snapshots updated to match. + - CLAUDE.md "Allocator construction" rule rewritten around the + by-value convention. + + 157/157 example tests + chess clean on macOS / iOS sim / Android + (`tools/verify-step.sh` ran green). + - **`xx ` borrows the operand's storage** (Option 3 in the protocol-erasure design discussion). Today's behavior — `xx ` heap-copies the value — was a silent footgun: @@ -242,7 +271,16 @@ Allocator value naturally. ## Log -- **2026-05-25 (latest)** — `xx ` semantics changed to borrow. +- **2026-05-25 (latest)** — Allocator `init` returns the state by + value. GPA / Arena / TrackingAllocator all changed; `Arena.deinit` + no longer self-deallocs. `UIPipeline.arena_a/_b` embedded as values; + `@self.arena_a` at the *Arena use site. `examples/50-smoke.sx` + Arena alloc counts dropped by 1 (no state struct alloc); FFI IR + snapshots regenerated to reflect new signatures (`-> ptr` → + `-> i64`/`-> void sret(...)`). CLAUDE.md "Allocator construction" + rewritten around the by-value convention. 157/157 + chess green + on all three platforms via `tools/verify-step.sh`. +- **2026-05-25 (penultimate)** — `xx ` semantics changed to borrow. Single change at `lower.zig:10334` (`buildProtocolErasure`) gated by new `isLvalueExpr` helper at `lower.zig:10322`. specs.md §3 ownership table extended (three modes: rvalue / lvalue / pointer). diff --git a/examples/126-xx-recover-then-dispatch.sx b/examples/126-xx-recover-then-dispatch.sx index def3da8..4e405f7 100644 --- a/examples/126-xx-recover-then-dispatch.sx +++ b/examples/126-xx-recover-then-dispatch.sx @@ -11,14 +11,14 @@ main :: () -> s32 { // Recover BEFORE first dispatch. recovered : *GPA = xx a; - print("recovered == gpa? {}\n", recovered == gpa); + print("recovered == gpa? {}\n", recovered == @gpa); p := a.alloc(64); print("alloc count after first alloc: {}\n", gpa.alloc_count); // Recover AFTER dispatch — still works. recovered2 : *GPA = xx a; - print("recovered2 == gpa? {}\n", recovered2 == gpa); + print("recovered2 == gpa? {}\n", recovered2 == @gpa); a.dealloc(p); print("alloc count after dealloc: {}\n", gpa.alloc_count); diff --git a/examples/135-xx-lvalue-borrows.sx b/examples/135-xx-lvalue-borrows.sx index f630572..cf0e09a 100644 --- a/examples/135-xx-lvalue-borrows.sx +++ b/examples/135-xx-lvalue-borrows.sx @@ -12,8 +12,7 @@ main :: () -> s32 { gpa := GPA.init(); - tracker_ptr := TrackingAllocator.init(xx gpa); - tracker := tracker_ptr.*; // dereference into a stack-local VALUE + tracker := TrackingAllocator.init(xx gpa); // value, stack-local // xx tracker — operand is an identifier (lvalue), so the protocol // borrows tracker's storage. No heap copy. Mutations propagate. diff --git a/examples/50-smoke.sx b/examples/50-smoke.sx index 286cb04..cfdb3da 100644 --- a/examples/50-smoke.sx +++ b/examples/50-smoke.sx @@ -1691,12 +1691,13 @@ END; // First chunk fits 80 usable bytes a1 := a.alloc(40); a2 := a.alloc(40); - // Counts: Arena struct itself + first chunk = 2 (Arena.init - // allocates its own state through the parent allocator). - print("arena chunks: {}\n", gpa3.alloc_count); // arena chunks: 2 + // Counts: just the first chunk = 1. Arena.init returns the + // state by value; the local IS the Arena struct, no parent + // allocation for the state itself. + print("arena chunks: {}\n", gpa3.alloc_count); // arena chunks: 1 // Overflow → new chunk a3 := a.alloc(16); - print("arena overflow: {}\n", gpa3.alloc_count); // arena overflow: 3 + print("arena overflow: {}\n", gpa3.alloc_count); // arena overflow: 2 // Verify memory works across chunks p1 : [*]u8 = xx a1; p3 : [*]u8 = xx a3; @@ -1704,11 +1705,12 @@ END; p3[0] = 99; print("arena a1: {}\n", p1[0]); // arena a1: 42 print("arena a3: {}\n", p3[0]); // arena a3: 99 - // Reset retains newest chunk + // Reset retains the first chunk arena.reset(); print("arena reset idx: {}\n", arena.end_index); // arena reset idx: 0 - print("arena reset gpa: {}\n", gpa3.alloc_count); // arena reset gpa: 2 - // Deinit frees all chunks + the Arena state itself + print("arena reset gpa: {}\n", gpa3.alloc_count); // arena reset gpa: 1 + // Deinit frees all chunks (caller's local is the state — no + // dealloc of the struct itself). arena.deinit(); print("arena deinit: {}\n", gpa3.alloc_count); // arena deinit: 0 } diff --git a/library/modules/allocators.sx b/library/modules/allocators.sx index ba07759..3f87f5f 100644 --- a/library/modules/allocators.sx +++ b/library/modules/allocators.sx @@ -28,18 +28,21 @@ impl Allocator for CAllocator { // --- GPA: general purpose allocator (malloc/free wrapper) --- // +// `init` returns the GPA by value. Caller binds it to a local; the +// local IS the allocator state, no heap-side allocation for the +// struct itself. `xx gpa` borrows the local under Option 3, so the +// Allocator protocol value's `ctx` points at the local. +// // Usage: -// gpa := GPA.init(); // *GPA +// gpa := GPA.init(); // GPA // push Context.{ allocator = xx gpa, data = null } { ... } // print("alloc count: {}\n", gpa.alloc_count); GPA :: struct { alloc_count: s64; - init :: () -> *GPA { - g : *GPA = xx libc_malloc(size_of(GPA)); - g.alloc_count = 0; - g; + init :: () -> GPA { + GPA.{ alloc_count = 0 }; } } @@ -56,12 +59,17 @@ impl Allocator for GPA { // --- Arena: multi-chunk bump allocator --- // +// `init` returns the Arena by value; the caller's local holds the +// state, no heap-side allocation for the struct itself. The arena's +// chunks ARE heap-allocated through the parent allocator, but those +// are owned by `deinit` (or `reset` for the non-first ones). +// // Usage: // gpa := GPA.init(); -// arena := Arena.init(xx gpa, 4096); // *Arena +// arena := Arena.init(xx gpa, 4096); // Arena // push Context.{ allocator = xx arena, data = null } { ... } -// arena.reset(); // direct method on *Arena -// arena.deinit(); +// arena.reset(); // free all chunks except the first +// arena.deinit(); // free every chunk ArenaChunk :: struct { next: *ArenaChunk; @@ -85,11 +93,8 @@ Arena :: struct { a.end_index = 0; } - init :: (parent_alloc: Allocator, size: s64) -> *Arena { - self : *Arena = xx parent_alloc.alloc(size_of(Arena)); - self.first = null; - self.end_index = 0; - self.parent = parent_alloc; + init :: (parent_alloc: Allocator, size: s64) -> Arena { + self : Arena = .{ first = null, end_index = 0, parent = parent_alloc }; self.add_chunk(size); self; } @@ -114,8 +119,8 @@ Arena :: struct { a.parent.dealloc(it); it = next; } - parent := a.parent; - parent.dealloc(xx a); + a.first = null; + a.end_index = 0; } } @@ -190,7 +195,7 @@ impl Allocator for BufAlloc { // // Manual opt-in pattern (compiler auto-wrap lands in Phase 5): // -// tracker := TrackingAllocator.init(context.allocator); // *TrackingAllocator +// tracker := TrackingAllocator.init(context.allocator); // TrackingAllocator // push Context.{ allocator = xx tracker, data = null } { // // ... user code allocates via tracker → delegates to the // // original context.allocator (libc-backed by default) ... @@ -210,13 +215,13 @@ TrackingAllocator :: struct { dealloc_count: s64; total_alloc_bytes: s64; - init :: (parent_alloc: Allocator) -> *TrackingAllocator { - t : *TrackingAllocator = xx parent_alloc.alloc(size_of(TrackingAllocator)); - t.parent = parent_alloc; - t.alloc_count = 0; - t.dealloc_count = 0; - t.total_alloc_bytes = 0; - t; + init :: (parent_alloc: Allocator) -> TrackingAllocator { + TrackingAllocator.{ + parent = parent_alloc, + alloc_count = 0, + dealloc_count = 0, + total_alloc_bytes = 0, + }; } leak_count :: (t: *TrackingAllocator) -> s64 { diff --git a/library/modules/ui/pipeline.sx b/library/modules/ui/pipeline.sx index 4d9414a..9594fed 100755 --- a/library/modules/ui/pipeline.sx +++ b/library/modules/ui/pipeline.sx @@ -18,11 +18,14 @@ UIPipeline :: struct { root: ViewChild; has_root: bool; - // Frame arena infrastructure. Both arenas are typed `*Arena` - // pointers (per the init-returns-typed-pointer API in - // allocators.sx). Cast to Allocator at use sites via `xx arena_a`. - arena_a: *Arena; - arena_b: *Arena; + // Frame arena infrastructure. Both arenas are embedded value-typed + // fields — Arena.init returns the state by value, so UIPipeline + // holds the storage directly. Internal code grabs `*Arena` via + // `@self.arena_a` when it needs to call methods through the + // protocol; consumers in `tick_with_body` cast to Allocator at the + // `push Context` site. + arena_a: Arena; + arena_b: Arena; frame_index: s64; body: Closure() -> View; has_body: bool; @@ -132,7 +135,7 @@ UIPipeline :: struct { } tick_with_body :: (self: *UIPipeline) { - build_arena : *Arena = if (self.frame_index & 1) == 0 then self.arena_a else self.arena_b; + build_arena : *Arena = if (self.frame_index & 1) == 0 then @self.arena_a else @self.arena_b; build_arena.reset(); // Reset render_tree nodes (backing is stale after arena reset) diff --git a/tests/expected/50-smoke.txt b/tests/expected/50-smoke.txt index 4ab0c8c..b41b5cb 100644 --- a/tests/expected/50-smoke.txt +++ b/tests/expected/50-smoke.txt @@ -389,12 +389,12 @@ bytes len: 3 --- allocators --- gpa allocs: 2 gpa final: 0 -arena chunks: 2 -arena overflow: 3 +arena chunks: 1 +arena overflow: 2 arena a1: 42 arena a3: 99 arena reset idx: 0 -arena reset gpa: 2 +arena reset gpa: 1 arena deinit: 0 buf pos: 48 buf overflow: 0 diff --git a/tests/expected/ffi-jni-call-03-methodid-sharing.ir b/tests/expected/ffi-jni-call-03-methodid-sharing.ir index 6af6d88..9f1e6ec 100644 --- a/tests/expected/ffi-jni-call-03-methodid-sharing.ir +++ b/tests/expected/ffi-jni-call-03-methodid-sharing.ir @@ -63,7 +63,7 @@ entry: } ; Function Attrs: nounwind -declare ptr @GPA.init(ptr) #0 +declare i64 @GPA.init(ptr) #0 ; Function Attrs: nounwind declare ptr @GPA.alloc(ptr, ptr, i64) #0 @@ -75,7 +75,7 @@ declare void @GPA.dealloc(ptr, ptr, ptr) #0 declare void @Arena.add_chunk(ptr, ptr, i64) #0 ; Function Attrs: nounwind -declare ptr @Arena.init(ptr, ptr, i64) #0 +declare void @Arena.init(ptr sret({ ptr, i64, { ptr, ptr, ptr } }), ptr, ptr, i64) #0 ; Function Attrs: nounwind declare void @Arena.reset(ptr, ptr) #0 @@ -102,7 +102,7 @@ declare ptr @BufAlloc.alloc(ptr, ptr, i64) #0 declare void @BufAlloc.dealloc(ptr, ptr, ptr) #0 ; Function Attrs: nounwind -declare ptr @TrackingAllocator.init(ptr, ptr) #0 +declare void @TrackingAllocator.init(ptr sret({ { ptr, ptr, ptr }, i64, i64, i64 }), ptr, ptr) #0 ; Function Attrs: nounwind declare i64 @TrackingAllocator.leak_count(ptr, ptr) #0 @@ -736,5 +736,3 @@ entry: } declare i64 @write(i32, ptr, i64) - - diff --git a/tests/expected/ffi-jni-call-04-jint-return.ir b/tests/expected/ffi-jni-call-04-jint-return.ir index 367e355..1807bd4 100644 --- a/tests/expected/ffi-jni-call-04-jint-return.ir +++ b/tests/expected/ffi-jni-call-04-jint-return.ir @@ -61,7 +61,7 @@ entry: } ; Function Attrs: nounwind -declare ptr @GPA.init(ptr) #0 +declare i64 @GPA.init(ptr) #0 ; Function Attrs: nounwind declare ptr @GPA.alloc(ptr, ptr, i64) #0 @@ -73,7 +73,7 @@ declare void @GPA.dealloc(ptr, ptr, ptr) #0 declare void @Arena.add_chunk(ptr, ptr, i64) #0 ; Function Attrs: nounwind -declare ptr @Arena.init(ptr, ptr, i64) #0 +declare void @Arena.init(ptr sret({ ptr, i64, { ptr, ptr, ptr } }), ptr, ptr, i64) #0 ; Function Attrs: nounwind declare void @Arena.reset(ptr, ptr) #0 @@ -100,7 +100,7 @@ declare ptr @BufAlloc.alloc(ptr, ptr, i64) #0 declare void @BufAlloc.dealloc(ptr, ptr, ptr) #0 ; Function Attrs: nounwind -declare ptr @TrackingAllocator.init(ptr, ptr) #0 +declare void @TrackingAllocator.init(ptr sret({ { ptr, ptr, ptr }, i64, i64, i64 }), ptr, ptr) #0 ; Function Attrs: nounwind declare i64 @TrackingAllocator.leak_count(ptr, ptr) #0 @@ -711,5 +711,3 @@ entry: } declare i64 @write(i32, ptr, i64) - - diff --git a/tests/expected/ffi-jni-call-05-jlong-return.ir b/tests/expected/ffi-jni-call-05-jlong-return.ir index 144643b..4c6c41d 100644 --- a/tests/expected/ffi-jni-call-05-jlong-return.ir +++ b/tests/expected/ffi-jni-call-05-jlong-return.ir @@ -61,7 +61,7 @@ entry: } ; Function Attrs: nounwind -declare ptr @GPA.init(ptr) #0 +declare i64 @GPA.init(ptr) #0 ; Function Attrs: nounwind declare ptr @GPA.alloc(ptr, ptr, i64) #0 @@ -73,7 +73,7 @@ declare void @GPA.dealloc(ptr, ptr, ptr) #0 declare void @Arena.add_chunk(ptr, ptr, i64) #0 ; Function Attrs: nounwind -declare ptr @Arena.init(ptr, ptr, i64) #0 +declare void @Arena.init(ptr sret({ ptr, i64, { ptr, ptr, ptr } }), ptr, ptr, i64) #0 ; Function Attrs: nounwind declare void @Arena.reset(ptr, ptr) #0 @@ -100,7 +100,7 @@ declare ptr @BufAlloc.alloc(ptr, ptr, i64) #0 declare void @BufAlloc.dealloc(ptr, ptr, ptr) #0 ; Function Attrs: nounwind -declare ptr @TrackingAllocator.init(ptr, ptr) #0 +declare void @TrackingAllocator.init(ptr sret({ { ptr, ptr, ptr }, i64, i64, i64 }), ptr, ptr) #0 ; Function Attrs: nounwind declare i64 @TrackingAllocator.leak_count(ptr, ptr) #0 @@ -711,5 +711,3 @@ entry: } declare i64 @write(i32, ptr, i64) - - diff --git a/tests/expected/ffi-jni-call-06-jdouble-return.ir b/tests/expected/ffi-jni-call-06-jdouble-return.ir index 040bbc9..12503b0 100644 --- a/tests/expected/ffi-jni-call-06-jdouble-return.ir +++ b/tests/expected/ffi-jni-call-06-jdouble-return.ir @@ -61,7 +61,7 @@ entry: } ; Function Attrs: nounwind -declare ptr @GPA.init(ptr) #0 +declare i64 @GPA.init(ptr) #0 ; Function Attrs: nounwind declare ptr @GPA.alloc(ptr, ptr, i64) #0 @@ -73,7 +73,7 @@ declare void @GPA.dealloc(ptr, ptr, ptr) #0 declare void @Arena.add_chunk(ptr, ptr, i64) #0 ; Function Attrs: nounwind -declare ptr @Arena.init(ptr, ptr, i64) #0 +declare void @Arena.init(ptr sret({ ptr, i64, { ptr, ptr, ptr } }), ptr, ptr, i64) #0 ; Function Attrs: nounwind declare void @Arena.reset(ptr, ptr) #0 @@ -100,7 +100,7 @@ declare ptr @BufAlloc.alloc(ptr, ptr, i64) #0 declare void @BufAlloc.dealloc(ptr, ptr, ptr) #0 ; Function Attrs: nounwind -declare ptr @TrackingAllocator.init(ptr, ptr) #0 +declare void @TrackingAllocator.init(ptr sret({ { ptr, ptr, ptr }, i64, i64, i64 }), ptr, ptr) #0 ; Function Attrs: nounwind declare i64 @TrackingAllocator.leak_count(ptr, ptr) #0 @@ -711,5 +711,3 @@ entry: } declare i64 @write(i32, ptr, i64) - - diff --git a/tests/expected/ffi-jni-call-07-jboolean-return.ir b/tests/expected/ffi-jni-call-07-jboolean-return.ir index a476315..b305f39 100644 --- a/tests/expected/ffi-jni-call-07-jboolean-return.ir +++ b/tests/expected/ffi-jni-call-07-jboolean-return.ir @@ -61,7 +61,7 @@ entry: } ; Function Attrs: nounwind -declare ptr @GPA.init(ptr) #0 +declare i64 @GPA.init(ptr) #0 ; Function Attrs: nounwind declare ptr @GPA.alloc(ptr, ptr, i64) #0 @@ -73,7 +73,7 @@ declare void @GPA.dealloc(ptr, ptr, ptr) #0 declare void @Arena.add_chunk(ptr, ptr, i64) #0 ; Function Attrs: nounwind -declare ptr @Arena.init(ptr, ptr, i64) #0 +declare void @Arena.init(ptr sret({ ptr, i64, { ptr, ptr, ptr } }), ptr, ptr, i64) #0 ; Function Attrs: nounwind declare void @Arena.reset(ptr, ptr) #0 @@ -100,7 +100,7 @@ declare ptr @BufAlloc.alloc(ptr, ptr, i64) #0 declare void @BufAlloc.dealloc(ptr, ptr, ptr) #0 ; Function Attrs: nounwind -declare ptr @TrackingAllocator.init(ptr, ptr) #0 +declare void @TrackingAllocator.init(ptr sret({ { ptr, ptr, ptr }, i64, i64, i64 }), ptr, ptr) #0 ; Function Attrs: nounwind declare i64 @TrackingAllocator.leak_count(ptr, ptr) #0 @@ -711,5 +711,3 @@ entry: } declare i64 @write(i32, ptr, i64) - - diff --git a/tests/expected/ffi-jni-call-08-jobject-return.ir b/tests/expected/ffi-jni-call-08-jobject-return.ir index 62a2e4d..a8508a5 100644 --- a/tests/expected/ffi-jni-call-08-jobject-return.ir +++ b/tests/expected/ffi-jni-call-08-jobject-return.ir @@ -61,7 +61,7 @@ entry: } ; Function Attrs: nounwind -declare ptr @GPA.init(ptr) #0 +declare i64 @GPA.init(ptr) #0 ; Function Attrs: nounwind declare ptr @GPA.alloc(ptr, ptr, i64) #0 @@ -73,7 +73,7 @@ declare void @GPA.dealloc(ptr, ptr, ptr) #0 declare void @Arena.add_chunk(ptr, ptr, i64) #0 ; Function Attrs: nounwind -declare ptr @Arena.init(ptr, ptr, i64) #0 +declare void @Arena.init(ptr sret({ ptr, i64, { ptr, ptr, ptr } }), ptr, ptr, i64) #0 ; Function Attrs: nounwind declare void @Arena.reset(ptr, ptr) #0 @@ -100,7 +100,7 @@ declare ptr @BufAlloc.alloc(ptr, ptr, i64) #0 declare void @BufAlloc.dealloc(ptr, ptr, ptr) #0 ; Function Attrs: nounwind -declare ptr @TrackingAllocator.init(ptr, ptr) #0 +declare void @TrackingAllocator.init(ptr sret({ { ptr, ptr, ptr }, i64, i64, i64 }), ptr, ptr) #0 ; Function Attrs: nounwind declare i64 @TrackingAllocator.leak_count(ptr, ptr) #0 @@ -711,5 +711,3 @@ entry: } declare i64 @write(i32, ptr, i64) - - diff --git a/tests/expected/ffi-jni-call-09-static.ir b/tests/expected/ffi-jni-call-09-static.ir index 3e64779..13c3156 100644 --- a/tests/expected/ffi-jni-call-09-static.ir +++ b/tests/expected/ffi-jni-call-09-static.ir @@ -61,7 +61,7 @@ entry: } ; Function Attrs: nounwind -declare ptr @GPA.init(ptr) #0 +declare i64 @GPA.init(ptr) #0 ; Function Attrs: nounwind declare ptr @GPA.alloc(ptr, ptr, i64) #0 @@ -73,7 +73,7 @@ declare void @GPA.dealloc(ptr, ptr, ptr) #0 declare void @Arena.add_chunk(ptr, ptr, i64) #0 ; Function Attrs: nounwind -declare ptr @Arena.init(ptr, ptr, i64) #0 +declare void @Arena.init(ptr sret({ ptr, i64, { ptr, ptr, ptr } }), ptr, ptr, i64) #0 ; Function Attrs: nounwind declare void @Arena.reset(ptr, ptr) #0 @@ -100,7 +100,7 @@ declare ptr @BufAlloc.alloc(ptr, ptr, i64) #0 declare void @BufAlloc.dealloc(ptr, ptr, ptr) #0 ; Function Attrs: nounwind -declare ptr @TrackingAllocator.init(ptr, ptr) #0 +declare void @TrackingAllocator.init(ptr sret({ { ptr, ptr, ptr }, i64, i64, i64 }), ptr, ptr) #0 ; Function Attrs: nounwind declare i64 @TrackingAllocator.leak_count(ptr, ptr) #0 @@ -708,5 +708,3 @@ entry: } declare i64 @write(i32, ptr, i64) - - diff --git a/tests/expected/ffi-jni-class-08-call.ir b/tests/expected/ffi-jni-class-08-call.ir index b73e07a..a75622d 100644 --- a/tests/expected/ffi-jni-class-08-call.ir +++ b/tests/expected/ffi-jni-class-08-call.ir @@ -61,7 +61,7 @@ entry: } ; Function Attrs: nounwind -declare ptr @GPA.init(ptr) #0 +declare i64 @GPA.init(ptr) #0 ; Function Attrs: nounwind declare ptr @GPA.alloc(ptr, ptr, i64) #0 @@ -73,7 +73,7 @@ declare void @GPA.dealloc(ptr, ptr, ptr) #0 declare void @Arena.add_chunk(ptr, ptr, i64) #0 ; Function Attrs: nounwind -declare ptr @Arena.init(ptr, ptr, i64) #0 +declare void @Arena.init(ptr sret({ ptr, i64, { ptr, ptr, ptr } }), ptr, ptr, i64) #0 ; Function Attrs: nounwind declare void @Arena.reset(ptr, ptr) #0 @@ -100,7 +100,7 @@ declare ptr @BufAlloc.alloc(ptr, ptr, i64) #0 declare void @BufAlloc.dealloc(ptr, ptr, ptr) #0 ; Function Attrs: nounwind -declare ptr @TrackingAllocator.init(ptr, ptr) #0 +declare void @TrackingAllocator.init(ptr sret({ { ptr, ptr, ptr }, i64, i64, i64 }), ptr, ptr) #0 ; Function Attrs: nounwind declare i64 @TrackingAllocator.leak_count(ptr, ptr) #0 @@ -711,5 +711,3 @@ entry: } declare i64 @write(i32, ptr, i64) - - diff --git a/tests/expected/ffi-jni-env-02-lexical-direct.ir b/tests/expected/ffi-jni-env-02-lexical-direct.ir index 4ed7a9e..7f5dd07 100644 --- a/tests/expected/ffi-jni-env-02-lexical-direct.ir +++ b/tests/expected/ffi-jni-env-02-lexical-direct.ir @@ -61,7 +61,7 @@ entry: } ; Function Attrs: nounwind -declare ptr @GPA.init(ptr) #0 +declare i64 @GPA.init(ptr) #0 ; Function Attrs: nounwind declare ptr @GPA.alloc(ptr, ptr, i64) #0 @@ -73,7 +73,7 @@ declare void @GPA.dealloc(ptr, ptr, ptr) #0 declare void @Arena.add_chunk(ptr, ptr, i64) #0 ; Function Attrs: nounwind -declare ptr @Arena.init(ptr, ptr, i64) #0 +declare void @Arena.init(ptr sret({ ptr, i64, { ptr, ptr, ptr } }), ptr, ptr, i64) #0 ; Function Attrs: nounwind declare void @Arena.reset(ptr, ptr) #0 @@ -100,7 +100,7 @@ declare ptr @BufAlloc.alloc(ptr, ptr, i64) #0 declare void @BufAlloc.dealloc(ptr, ptr, ptr) #0 ; Function Attrs: nounwind -declare ptr @TrackingAllocator.init(ptr, ptr) #0 +declare void @TrackingAllocator.init(ptr sret({ { ptr, ptr, ptr }, i64, i64, i64 }), ptr, ptr) #0 ; Function Attrs: nounwind declare i64 @TrackingAllocator.leak_count(ptr, ptr) #0 @@ -709,5 +709,3 @@ entry: } declare i64 @write(i32, ptr, i64) - - diff --git a/tests/expected/ffi-objc-call-03-selector-sharing.ir b/tests/expected/ffi-objc-call-03-selector-sharing.ir index 8cd41e5..8eac771 100644 --- a/tests/expected/ffi-objc-call-03-selector-sharing.ir +++ b/tests/expected/ffi-objc-call-03-selector-sharing.ir @@ -64,7 +64,7 @@ entry: } ; Function Attrs: nounwind -declare ptr @GPA.init(ptr) #0 +declare i64 @GPA.init(ptr) #0 ; Function Attrs: nounwind declare ptr @GPA.alloc(ptr, ptr, i64) #0 @@ -76,7 +76,7 @@ declare void @GPA.dealloc(ptr, ptr, ptr) #0 declare void @Arena.add_chunk(ptr, ptr, i64) #0 ; Function Attrs: nounwind -declare ptr @Arena.init(ptr, ptr, i64) #0 +declare void @Arena.init(ptr sret({ ptr, i64, { ptr, ptr, ptr } }), ptr, ptr, i64) #0 ; Function Attrs: nounwind declare void @Arena.reset(ptr, ptr) #0 @@ -103,7 +103,7 @@ declare ptr @BufAlloc.alloc(ptr, ptr, i64) #0 declare void @BufAlloc.dealloc(ptr, ptr, ptr) #0 ; Function Attrs: nounwind -declare ptr @TrackingAllocator.init(ptr, ptr) #0 +declare void @TrackingAllocator.init(ptr sret({ { ptr, ptr, ptr }, i64, i64, i64 }), ptr, ptr) #0 ; Function Attrs: nounwind declare i64 @TrackingAllocator.leak_count(ptr, ptr) #0 @@ -792,5 +792,3 @@ entry: store ptr %selN, ptr @OBJC_SELECTOR_REFERENCES_release, align 8 ret void } - - diff --git a/tests/expected/ffi-objc-call-06-sret-return.ir b/tests/expected/ffi-objc-call-06-sret-return.ir index 121af6a..0afe4cb 100644 --- a/tests/expected/ffi-objc-call-06-sret-return.ir +++ b/tests/expected/ffi-objc-call-06-sret-return.ir @@ -249,7 +249,7 @@ entry: } ; Function Attrs: nounwind -declare ptr @GPA.init(ptr) #0 +declare i64 @GPA.init(ptr) #0 ; Function Attrs: nounwind declare ptr @GPA.alloc(ptr, ptr, i64) #0 @@ -261,7 +261,7 @@ declare void @GPA.dealloc(ptr, ptr, ptr) #0 declare void @Arena.add_chunk(ptr, ptr, i64) #0 ; Function Attrs: nounwind -declare ptr @Arena.init(ptr, ptr, i64) #0 +declare void @Arena.init(ptr sret({ ptr, i64, { ptr, ptr, ptr } }), ptr, ptr, i64) #0 ; Function Attrs: nounwind declare void @Arena.reset(ptr, ptr) #0 @@ -288,7 +288,7 @@ declare ptr @BufAlloc.alloc(ptr, ptr, i64) #0 declare void @BufAlloc.dealloc(ptr, ptr, ptr) #0 ; Function Attrs: nounwind -declare ptr @TrackingAllocator.init(ptr, ptr) #0 +declare void @TrackingAllocator.init(ptr sret({ { ptr, ptr, ptr }, i64, i64, i64 }), ptr, ptr) #0 ; Function Attrs: nounwind declare i64 @TrackingAllocator.leak_count(ptr, ptr) #0 @@ -3604,5 +3604,3 @@ entry: store ptr %sel, ptr @OBJC_SELECTOR_REFERENCES_tripleValue, align 8 ret void } - -