4 ms·
One reaction, with the caveat that I may have misunderstood some or all of this: One of the great advantages of manual memory management is that it does not re
by millstone 9y ago
One reaction, with the caveat that I may have misunderstood some or all of this:
One of the great advantages of manual memory management is that it does not require a runtime that can identify all live references. A pointer may be cast in an int, NaN-boxed, etc. - no problem.
In this scheme the "promoting live objects" phase works by copying objects to a new allocation. Crucially, this avoids the ABA problem by always walking forwards in memory. Second, it does not attempt to eagerly update all references - instead it permits references to become dangling. So let's say we have a dangling NaN-boxed pointer.
Now later, our NaN-boxed dangler is unpacked and dereferenced. This causes a SIGSEGV since the page is no longer readable. The signal handler notices it and attempts to fix it up.
IMO this is where things go off the rails:
> If the object was promoted, the handler scans all registers and the current stack frame, and patches all stale references to promoted objects.
We were so close, but now the runtime needs to be able to distinguish references to promoted objects and values that happen to share their address. We almost got away without stack maps.
> Therefore, we modify the compiler to emit meta-data describing the location of heap references in registers and on the stack for every load and store instruction (instead of just gc safe points)
Ok I give up. Now we're just building a garbage collector.
This does not help our NaN-boxing example. There's no way to easily inform the NaN-boxed value that it needs to update; we could hack it by comparing the register's value after the dereference, but that solution is hard to love.
IMO more interesting would be to modify the compiler to require all dereferences to be updatable, and propagate that back through to the original reference. In C++ speak we could imagine this:
void *dereference(void *&ptr) {...}
and force the clients to deal with the fallout of back-propagating the new value. It's a lot of work, but the carrot is "pointers cannot dangle and you don't need a GC and you don't need Rust-style borrow checking" which sounds pretty rad.
Anyways seems interesting but could be bolder?
- tomp 9y agoNaN-boxing and other types of pointer casting are fundamentally incompatible with this approach, because it doesn’t support pointer arithmetic (if you could cast a pointer to int and back, you could do pointer arithmetic).
- snaky 9y agoDo we know yet any ways to get "pointers cannot dangle and you don't need a GC and you don't need Rust-style borrow checking" other than region-based memory management (MLton, Cyclone)?
- kod 9y agoHow are you distinguishing Rust from Cyclone? Seems like the same foundation.
- frankmcsherry 9y agoOwnership / affine types? It's hard to say, because some folks will jump up and down and say "reference counting is GC!", and then some folks could just as justifiably say "regions are GC!"
- sitkack 9y agoAnything less than being able to track memory lifetime at compilation is GC. Some folks will jump up and down and say "variable sized allocation is sloppy!" GC is a spectrum on which reference counting lives. Is there an arch that supports bidirectional references?
- snaky 9y agoMaybe linear types actually, affine is not enough?
- frankmcsherry 9y agoI suspect affine is fine, but I'm not an authority. The distinction (as I understand it) is only that linear types require that you consume all instances, whereas affine allow you to leak if you want. Leaking doesn't seem to lead to dangling pointers, but I've been wrong before.