@nytpu Hmm, so of a certain way is like how struct returns in C work? The caller owns the space that the callee constructs their return value in, but in this case is a variable amount of space. Can do in Forth or PostScript or Perl or HP RPL: construct variable-size values on the operand stack for the caller to consume, because the operand stack is separate from the return-address stack. In Perl is an implementation detail but is still how it works.
Thank you very much, is very interesting!
@nytpu Thinking further: if A calls B, the which is going to return to it some variable-sized thing (thing 1) on the secondary stack, and B calls C, the which returns to it a variable-sized thing 2 on the secondary stack, and C returns to B, and B starts allocating thing 1, means B must consume and deallocate thing 2 first? Otherwise, thing 1 will be pushed after thing 2 on the stack, right? So thing 1 gets deallocated when thing 2 does, before B can return it to A?
In Perl, RPL, PostScript, and Forth, the answer is, yes, B must remove thing 2 from the operand stack stack before it can start creating thing 1.
@radehi
In GNAT (the GCC Ada compiler) actually operates a bit like an arena allocator that supports incremental deallocation. When an object goes lexically out of scope in code then the compiler will mark it as unused, and then try to “roll back” the secondary stack as far as possible without clobbering live data (potentially rolling back other regions marked unused if they weren't cleaned up before). Since it's used in primarily situations where stuff would normally be stack-allocated but can't be allocated by the caller because the size is unknown (like returning arrays from a function). They are scoped similarly to any other stack-allocated object, just in a different region so they can persist through a
return
from a function call.The biggest issue would be non-local pointers to the stack object, but if you do use them with secondary stack objects it has the same caveats as a pointer to any other stack object. But Ada's design generally resists pointers being passed up the call stack (which the compiler is often overly-scrupulous in checking too)
Details on the specific internals here: https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=gcc/ada/libgnat/s-secsta.ads;h=62e1c0bfdb3681c37ef5fc314f8ff8ada81fea77;hb=HEAD#l115