Stream: compiler development

Topic: allocators


view this post on Zulip Brendan Hansknecht (Oct 12 2025 at 02:46):

What is our general opinion around passing both gpa and arena to many functions. I noticed that we have a solid number of tiny allocations going to the gpa (like paths and such). Generally it is better for those to got to an arena instead. On top of that, we have some memory leaks that I am fixing up and in a few case, I think it might be nicer to put those things in the arena and not need to worry about them not being freed.


My general thought is that this is best practice, but definitely a bit inconvenient. Definitely not something that needs to be done of first pass or anything. I'm just in general thinking of cleaning up some locations and also trying to explicitly name allocators as gpa or arena to make it clearer what is expected. Of course for agnostic functions, it would stay as allocator.

Thoughts?

view this post on Zulip Brendan Hansknecht (Oct 12 2025 at 02:58):

Given we make both the gpa and the arena right at the start of main, maybe we should just eventually switch to a struct that contains both. That way, all functions can just choose what makes sense for each allocation?

const Allocators = struct {
    gpa: Allocator,
    arena: Allocator,
}

view this post on Zulip Brendan Hansknecht (Oct 12 2025 at 03:01):

This is kinda what zig does in their compiler, just at a per stage basis:
https://github.com/ziglang/zig/blob/87c18945c207c77b5bc84123a7ba043b848bbabb/src/Sema.zig#L43-L46

view this post on Zulip Brendan Hansknecht (Oct 12 2025 at 03:02):

/// Alias to `zcu.gpa`.
gpa: Allocator,
/// Points to the temporary arena allocator of the Sema.
/// This arena will be cleared when the sema is destroyed.
arena: Allocator,

Though I guess they have a perf stage arena instead of a global arena here. I think for all the paths and other things we would have global arena for roc. Separately we could consider per stage arenas for roc, but I think we already have our scratch buffers which kinda fill that role.

view this post on Zulip Brendan Hansknecht (Oct 12 2025 at 03:13):

Also, context: https://github.com/roc-lang/roc/actions/runs/18436673436/job/52531070396?pr=8285

These are some memory leaks we currently have that probably should be in an arena anyway.

view this post on Zulip Richard Feldman (Oct 12 2025 at 03:37):

I like that idea!

view this post on Zulip Richard Feldman (Oct 12 2025 at 03:37):

+1 to more arena usage

view this post on Zulip Richard Feldman (Oct 12 2025 at 03:38):

could we call it like scratch or something?

view this post on Zulip Richard Feldman (Oct 12 2025 at 03:38):

since sometimes we use an arena allocator for gpa :smile:

view this post on Zulip Brendan Hansknecht (Oct 12 2025 at 03:44):

In the most extreme I would see 3 categories:

  1. gpa only for things that grow and reallocate like all our arraylists
  2. arena for things that like the whole program (or at least for multiple stages) but are tiny and not worth deallocating.
  3. scratch anything that is specific to a compiler stage but is static size or exceptionally unlikely to grow and thus fine to consider static. Will be fully cleared between each compiler stage.

That said, I'm not sure it is worth having all 3. Might just be inconvenient to wrangle. I'm also not sure how much stuff we have in category 3. If it isn't a lot, it can still just go into 2 instead.

And I'm open to whatever name. I was assuming functions would have allocs: Allocators as the first arg and then use allocs.gpa and allocs.arena (or whatever other name).

view this post on Zulip Brendan Hansknecht (Oct 12 2025 at 03:45):

Do you have a sense of how much stuff we have in category 3? I know that some of the stages have scratch space, but to my understand it is dynamically resizing, so probably should be under the gpa instead of in a true scratch buffer for the specific stage.

view this post on Zulip Brendan Hansknecht (Oct 12 2025 at 03:47):

Of note, I think zig used to have the 3 allocator system. They called them gpa, perm_arena, and arena. But they remove the perm_arena actually.

view this post on Zulip Richard Feldman (Oct 12 2025 at 15:11):

interesting! We actually have a bunch of scratch array lists that are used as typed stacks of temporary work, so maybe those are sufficient

view this post on Zulip Brendan Hansknecht (Oct 12 2025 at 16:27):

Yeah, as long as they are amortizing the allocations, they are probably fine. Different ways to essentially do the same thing.

Like the other design with an arena would probably be to free form allocate into the arena instead of having the n different array lists for scratch.

I would say that for now these are a fine scratch solutions and I just want to add a global arena for all the random tiny allocations that we really don't care about.

view this post on Zulip Richard Feldman (Oct 12 2025 at 16:46):

sounds good to me, go for it!

view this post on Zulip Brendan Hansknecht (Oct 15 2025 at 23:45):

Add new Allocators struct to pass gpa and arena to more functions #8285

Really only got to updating main.zig (tons of paths and things now using the arena). That said, I think it is big enough that it is worth submitting. And follow ups and expand to more files.


Last updated: Oct 18 2025 at 12:13 UTC