Posted by pizlonator 9/5/2025
Because if they get converted to integers and then stored to the heap then they lose their capability. So accesses to them will trap and the GC doesn’t need to care about them.
Also it’s not “Dijkstra accurate”. It’s a Dijkstra collector in the sense that it uses a Dijkstra barrier. And it’s an accurate collector. But these are orthogonal things
Let's say I malloc(42) then print the address to stdout, and then do not otherwise do anything with the pointer. Ten minutes later I prompt the user for an integer, they type back the same address, and then I try to write 42 bytes to that address.
What happens?
Edit: ok I read up on GC literature briefly and I believe I understand the situation.
"conservative" means the garbage collector does not have access to language type system information and is just guessing that every pointer sized thing in the stack is probably a pointer.
"accurate" means the compiler tells the GC about pointer types, so it knows about all the pointers the type system knows about.
Neither of these are capable of correctly modeling the C language semantics, which allows ptrtoint / inttoptr. So if there are any tricks being used like xor linked lists, storing extra data inside unused pointer alignment bits, or a memory allocator implementation, these will be incompatible even with an "accurate" garbage collector such as this.
I should add, this is not a criticism, I'm just trying to understand the design space. It's a pretty compelling trade offer: give up ptrtoint, receive GC.
In particular, check out the sections called "Laundering Pointers As Integers" and "Laundering Integers As Pointers".
> This is because the capability is not stored at any addresses that are accessible to the Fil-C program.
How are they stored? Is the GC running in a different process?
https://github.com/protocolbuffers/protobuf/blob/cb873c8987d...
// This somewhat silly looking add-and-subtract behavior provides provenance
// from the original input buffer's pointer. After optimization it produces
// the same assembly as just casting `(uintptr_t)ptr+input_delta`
// https://godbolt.org/z/zosG88oPn
size_t position =
(uintptr_t)ptr + e->input_delta - (uintptr_t)e->buffer_start;
return e->buffer_start + position;
It does use the implementation defined behavior that a char pointer + 1 casted to uintptr is the same as casting to uintptr then adding 1.Code that strives to preserve provenance works in Fil-C
1. https://twitter.com/6thgrade4ever/status/1433519577892327424
Half of my hair turned white while trying to understand this.
This article glosses over what I consider the hardest part - the enter/exit functionality around native functions may that block (but which must touch the allocator).
No it's not, not even close.
> This article glosses over what I consider the hardest part - the enter/exit functionality around native functions may that block (but which must touch the allocator).
Yeah, that part is hard, and maybe I'll describe it in another post.
Look for `filc_enter` and `filc_exit` in https://github.com/pizlonator/fil-c/blob/deluge/libpas/src/l...
So this is probably not great for functional/deeply-recursive code I guess?
The stack scan is really fast. There's not a lot of logic in there. If you max out the stack height limit (megabytes of stack?) then maybe that means milliseconds of work to scan that stack. That's still not bad.
Probably not. Your game would be inches of stack away from crashing
(I’m not trying to BS my way here - I’m explaining the reason why on the fly GC optimization almost never involves doing stuff about the time it takes to scan stack. It’s just not worth it. If it was, we’d be seeing a lot of stacklet type optimizations.)
On x86_64 you need about 10k function deep stack, all of them with the 14 GPs filled with pointers -to have an 1MB stack.
(It would be a linear scan if I was just conservatively scanning, but then I'd have other problems.)
This is one of the most counterintuitive parts of GC perf! You'd think that the stack scan had to be a bottleneck, and it might even be one in some corner cases. But it's just not the longest pole in the tent most of the time, because you're so unlikely to actually have a 1MB stack, and programs that do have a 1MB stack tend to also have ginormous heaps (like many gigabytes), which then means that even if the stack scan is a problem it's not the problem.
I don't know, maybe the fact that I'm disagreeing with someone who knows a lot more than I do about the issue should be a warning sign that I'm probably wrong?
To be fair, FUGC doesn’t currently let you do that. The GC runs in a separate thread and soft handshakes at various points, which cause your game thread to react at poll checks and exits that might not be at end of tick.
But I could add a feature that lets you to force handshake responses to be at end of tick! That sounds like a good idea
Never heard of this one, looking forward to diving in this weekend.