-
Notifications
You must be signed in to change notification settings - Fork 6
Virtual Registers
Compilers often use named slots to represent locations where a value will be stored. These named slots are virtual in the sense that they may be mapped in the end to a CPU register or a function's stack frame. In our compiler we use the term Register to denote such a virtual slot.
Instructions can define a Register, or use one or more Registers.
There are several requirements we need to meet when deciding how to represent these virtual registers.
The first requirement is that every variable declared in a function must get a unique slot. In the source language a variable name may be reused in multiple scopes, but from a compiler's perspective, each variable declaration is unique, because each has a unique lifetime and type.
Consider this example:
func foo(x: Int)
{
if (x == 0)
{
var i = 0
}
if (x == 1)
{
var i = 1
}
}
The variable i has two defined instances, each has a different lifetime. From a compiler's standpoint, each instance of i must be mapped to a distinct virtual register.
The second requirement is that although each virtual register is unique - they may get mapped to the same physical slot eventually. If we are targeting a CPU then this final location may be a CPU register or a memory location in the function's stack frame. In optvm module, we target an abstract virtual machine, a VM that is. This abstract machine supports unlimited number of virtual registers per function. Each such register is a 64-bit slot in the function's stack frame. We call this a frameSlot in the implementation.
So in the above example, after compiling the two virtual registers that represent i may end up pointing to the same frameSlot.
| source variable | virtual register ID | frame slot |
|---|---|---|
| i | 1 | 0 |
| i | 2 | 0 |