Hi Folks,
I wanted to share the seed of an idea I have been ruminating on for a while, and that is being able to return alloca memory from a function. Note: this is an edit to a message sent to the GCC mailing list.
I think it’s trivially possible to return alloca memory from a function by hacking the epilogue to unlink the frame pointer but not pop the stack. Stack traces should work fine and it would look like the parent function called an alloca covering the fixed child frame and the alloca or VLA that it returned.
I tend to use alloca a lot in my programming style as I like to avoid the heap. I have a style where I run a loop with a size pass, then call alloca, then run the loop again without suppressing writes. I would like to be able to return, initially, one alloca to a parent function, but more than one should also be possible.
I haven’t thought a lot about the mechanics of how one might expose this to a user to signal to the compiler to suppress popping the stack. I imagined one could return a pointer or structure with _Stack qualifiers to signal that stack memory from the child frame is being returned. There is a separate conversation about _Stack, _Data, _Heap, and _Thread (TLS) memory realm qualifiers on pointers for type reflection and checkers that perhaps translate underneath to address spaces.
This is a somewhat brief note as there is a deeper conversation about compacting stack arenas that would be possible if one had a runtime and compile time reflection API for frame metadata, where the frame is expressed as an anonymous C structure perhaps accessible via a function on a reference to a function. If you had frame metadata, you could emit one or many memmove calls. trivially one just needs the fixed frame length to recover the fixed frame stack memory, and, with full frame reflection, it may be possible to defer to a CRT routine in case two or more stack pointers are returned, assuming the compiler tracks sizes of alloca calls, although there are some issues with safe stack mentioned at the end of this post. Ideally, the reflection API would also be constexpr-able meaning the runtime compaction could be inlined. This wouldn’t work for types with embedded pointers to stack unless one has a full single-thread compacting GC that uses type metadata, but I tend to use indices anyway. But unlike heap compaction, it doesn’t have to worry about races.
The beauty of stack arenas, and compacting stack arenas, as a subtype, are many. Firstly the memory has no thread aliasing issues unlike heap memory, meaning analysis is much simpler and there are no pause issues like there are for GC. Secondly, it would be quite a neat way to constexpr variable-length arrays or a _List type because, unlike malloc, it’s much easier to reason about. This would also be useful in C++ to inline returning (initializer) lists as memory SSA is cleaner when not calling malloc because technically malloc memory is neither alias-free nor race-free, and memory SSA is taking some liberties.
I also have a relatively complete C reflection API that could be used as a starting point for C reflection. It currently works as an LLVM plugin. It would also be great to have access to the frame of a function as an anonymous structure. The API is only runtime at present and it could benefit from a for comprehension as looping on variable length lists is a little clumsy as I need to fetch the size first then pass in a pointer. If I could return alloca memory in a structure that would largely solve this problem with a more elegant interface by returning a structure with a length and a pointer to alloca memory.
There is a secondary question here, and that is, is there a way to reflect on the frame of a function in a plugin, perhaps as an anonymous structure? I guess this is complicated because one would need DWARF-style program-points as the frame could vary at different program-points/line-numbers due to frame memory reuse, although, I guess it should be trivially possible to get the size of the extent of the fixed and variable portions of the frame, assuming the use of VLAs. I’m thinking one could make some builtins like:
_buitin_fn_stack_fixed_begin_buitin_fn_stack_fixed_end_buitin_fn_stack_var_begin_buitin_fn_stack_var_end
They could be used in a macro, or stack inline function, to emit one or more memmove calls. It would also need to be safe-stack aware. It would also be nice if the CRT could expose a way to get the beginning and end pointers of the stack for the current thread and safe-stack if compiled using safe stacks. In the case of safe-stack, the variable portion would be in a different arena and the runtime would need to be aware of this and suppress memmove to compact over the fixed portion of the frame. Although compaction is not strictly necessary for this to work, it’s a nice to have.
There is another topic here accessing-stack-frame-after-returning-from-the-function that is related, but I have created a new topic because I am proposing to call these stack arenas and I give more details.
Regards,
Michael