I am trying to port an historical functional language interpreter (KRC for EMAS) to modern systems (C for Unix) and it has a garbage collector that expects to be able to scan the stack for pointers into the heap to know which pointers it must relocate when objects in the heap are moved during a GC. For this to work, all function arguments and local variables that point into the heap must be found in the stack.
Now, there was a time when the "register" keyword meant "you can put this variable in a register if you like" and otherwise it was on the stack, but nowadays all (GCC, Clang, Tinyc/tcc) C compilers seem to put local variables into registers regardless, with no way to disable this behaviour and the result is that that the GC is missing out on some values belonging to in-progress functions, failing to preserve them and corrupting the heap.
Is there a way to tell any of these compilers to use the original C semantics, whereby all local variables are on the stack unless you say "register"?
I have a few warty "solutions":
which all seem to improve matters, but are awfully hacky and unreliable.
Is there a better way to achieve the required result, of ensuring that all function parameters and local variables will be on the stack?
The stack is used for dynamic memory allocation, and local variables are stored at the top of the stack in a stack frame. A frame pointer is used to refer to local variables in the stack frame.
Advantages of using Stack When a function is called the local variables are stored in a stack, and it is automatically destroyed once returned. A stack is used when a variable is not used outside that function. It allows you to control how memory is allocated and deallocated. Stack automatically cleans up the object.
Local variables are stored in registers in most cases, because registers are pushed and poped from stack when you make function calls It looks like they are on stack.
The stack is memory like everything else and what is pushed on the stack is a reserved area of a specific size, the variables just live inside this space but they are just memory accesses as any other, the whole memory area is removed when the method returns. Think of the stack as a stack of papers.
I suppose that you use a kind of "mark and sweep" GC. In such case you only need to save registers at the moment when marking phase starts. My advise is to examine your GC, find the place where the "mark and sweep" operation starts and to put a code placing all registers into an accessible memory here. setjmp
is a semi-portable way to achieve this (unless you are working on sparc).
Ok, that is an odd GC; well, you might have use for the volatile
keyword.
It was originally meant for things like memory-mapped devices, where you'd want to force your compiler not to optimize away a variable. It's use and abuse has been a long standing topic of discussion.
Is there a better way to achieve the required result
Really really hard to answer. On one hand: obviously, yes: don't let your GC rely on things that can't be relied upon. But that means rewriting it. On the other hand: if things like additional code to ensure stack placement work, then why the hell not go for it? It's not like you're code-porting a historical interpreter for performance.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With