Execution context of a Smalltalk method is represented by an instance of Context class. It holds all information required to execute a method:
Pointer to a method which is used to access bytecodes and literals
Current instance (self) and provided arguments
Storage for temporary values
Stack of intermediate values
Pointer to current stack top
Pointer to the next instruction
Pointer to a previous context in chain
Jit version of VM takes advantage of hardware stack and links intermediate values directly. This allows us to eliminate the value stack completely. Instances of Context that are created during jit execution are allocated on the hardware stack. Even their stack, bytePointer and stackTop fields aren't initialized and left simply as nil.
However, everything has it's cost. In particular, every intermediate value created during jit execution should be protected in terms of GC safety. If garbage collection occur, it may effectively change the location of many objects rendering their pointers completely unusable unless protected. Much worse, locations of intermediate values may be stored in register, not in memory. If GC will not be able to track such values it will not update them.
Problem is solved by introducing so called shadow stack: a linked list of pointers to a stack frames that may contain intermediate values. GC knows about shadow stack and traverse it when garbage collection occurs. It is jit code generator responsibility to maintain the shadow stack along method execution and to re-load the values which may be changed since last GC. Finally, when method is finished and a value need to be returned shadow stack is unwound. This also happens during exception propagation.
The problem
Shadow stacks work well, but their maintenance takes time. Top entry in the shadow stack is located in a global variable which is not very flexible and not scalable when we start think about threading. Finally, shadow stack is redundant. In fact, it duplicates all information that may be deduced by looking at context instances. We're dealing with fully reflexive object oriented language, by the way :)
Let's try to use existing Smalltalk objects and solve the issue on our own.
The solution
The only purpose of shadow stack is to pin point locations on the hardware stack which should be tracked by GC. We know that context objects are tracked by GC, have unused fields and are accessible from the jit code, hmm…
Okay, so here's the deal:
In the method prologue another array object is allocated on the stack
Pointer to array is stored at… well… the stack field of the context
Intermediate values are stored in that array
Cleanup is done automatically: just throw away the stack frame!
Simple as it is. We even may pin point the locations of spilled registers that are known to hold object pointers and update them too. In that case it will be redundant to protect the context and self pointers!
Overview
Execution context of a Smalltalk method is represented by an instance of
Context
class. It holds all information required to execute a method:Jit version of VM takes advantage of hardware stack and links intermediate values directly. This allows us to eliminate the value stack completely. Instances of
Context
that are created during jit execution are allocated on the hardware stack. Even theirstack
,bytePointer
andstackTop
fields aren't initialized and left simply asnil
.However, everything has it's cost. In particular, every intermediate value created during jit execution should be protected in terms of GC safety. If garbage collection occur, it may effectively change the location of many objects rendering their pointers completely unusable unless protected. Much worse, locations of intermediate values may be stored in register, not in memory. If GC will not be able to track such values it will not update them.
Problem is solved by introducing so called shadow stack: a linked list of pointers to a stack frames that may contain intermediate values. GC knows about shadow stack and traverse it when garbage collection occurs. It is jit code generator responsibility to maintain the shadow stack along method execution and to re-load the values which may be changed since last GC. Finally, when method is finished and a value need to be returned shadow stack is unwound. This also happens during exception propagation.
The problem
Shadow stacks work well, but their maintenance takes time. Top entry in the shadow stack is located in a global variable which is not very flexible and not scalable when we start think about threading. Finally, shadow stack is redundant. In fact, it duplicates all information that may be deduced by looking at context instances. We're dealing with fully reflexive object oriented language, by the way :)
Let's try to use existing Smalltalk objects and solve the issue on our own.
The solution
The only purpose of shadow stack is to pin point locations on the hardware stack which should be tracked by GC. We know that context objects are tracked by GC, have unused fields and are accessible from the jit code, hmm…
Okay, so here's the deal:
stack
field of the contextSimple as it is. We even may pin point the locations of spilled registers that are known to hold object pointers and update them too. In that case it will be redundant to protect the context and self pointers!