Closed apt1002 closed 3 years ago
An analysis of goals:
Slot
s (unbounded in number) to be on the stack.Register
s and Slot
s to be dead when Mijit is not running.Register
s and Slot
s to be live in the root Machine::State
s, and we want to specify the live set in an ergonomic way.Machine
to choose the Save
data structure used to persist the state of the VM when Mijit is not running.Goal 1 deserves an issue of its own (#26) which also covers goal 2.
The combination of goals 2 and 3 motivates the prologue and epilogue code, whose purpose it to marshal data between the persistent state and the live set.
Goal 4 does not require any support from Mijit beyond providing a single persistent pointer to the prologue and epilogue code. The Machine
can use it as a pointer to a Save
if it wants; Mijit does not need to understand. So Machine::Save
is an unnecessary concept.
Goal 5 essentially boils down to sharing between the persistent state and the live set. This can be done by reference, e.g. via the pointer to the Save
. However, it is cheap and probably useful to provide some state that is shared "by value" too. I propose a constant-sized array of words (Global
s?) that can be accessed as Value
s (alongside Register
s and Slot
s) but which are always live and which persist when Mijit is not running. This is a mild generalisation of the single persistent pointer required by goal 4.
In summary, I propose the following design:
Global
to code::Value
, alongside Register
and Slot
, to represent a 64-bit word that persists when Mijit is not running. This deserves its own issue (#27).Machine::Save
does not exist.Machine::values()
does not exist. Instead Mijit defines a set of 64 Value
s: the first 12 Register
s and the first 52 Slot
s. It is not necessary to include Global
s, which are always live.Machine::num_globals()
returns the number of Global
s that the Machine
would like to use. This resurrects an earlier Mijit feature, with the difference that the Global
s are not considered to be a subset of the Slot
s.Machine::get_code(state)
returns (among other things) a 64-bit mask indicating the Value
s that are live in state
. The bitmask selects from the Mijit-defined set.Machine::prologue()
returns a list of Mijit Action
s to marshal data from the Global
s to the live values. It is not passed anything on entry. On exit it must have initialised all Value
s that are live in any State
.Machine::epilogue()
returns a list of Mijit Action
s to marshal data from the live values back to the Global
s. On entry, it gets all Value
s that are live in any State
; those that are dead are set to a dummy value (0xdeaddeaddeaddead
). On exit nothing is live.
Allow
code::Machine
s to define a "prologue" (code to execute on entry to Mijit) and an "epilogue" (code to execute on exit). I propose the following design:Machine::Save
is a type of theMachine
's choosing, suitable for saving the state which persists when Mijit is not running.Machine::values()
is a list of up to 64Value
s, which includes all those that are live at anyMachine::State
.Machine::get_code(State)
returns (among other things) the liveValue
s at theState
using a 64-bit mask, with one bit for each ofvalues()
.Save
, and specifies the initialState
. After saving the callee-saves registers, Mijit puts the pointer inRegister(0)
and runs the prologue, before entering the State. The prologue is expected to initialise allvalues()
.values()
to a dummy value (0xdeaddeaddeaddead
) and runs the epilogue. It then reloads all the callee-saves registers and returns the finalState
to the caller.Having prologue and epilogue code is helpful for the following reasons:
Machine
s choosing, as opposed to one of Mijit's choosing. This plays better with other code.Slot
s can be dead when Mijit is not running, just like the Registers. Therefore, we no longer need a global array ofSlot
s, and can instead use the system stack. This saves a precious Register, and allows us to make use ofpush
instructions.Register
s as well asSlot
s to be live, without fear that an exception will corrupt them.In principle, the Machine could have a separate prologue and epilogue tailored for each
State
, but I think that would be more annoying than useful. It would also provide places for obscure bugs to lurk. Instead, I suggest that we have just one prologue and epilogue, shared by all States, as described above.I am content to impose a limit of 64
values()
because they are expensive; they are marshalled to/from theSave
every time you enter or exit Mijit. Only one persistentValue
is actually needed: the pointer to theSave
. The others are just a Register-allocation hint, i.e. an optimisation. If you are using more than 64, you're not using them right.