Write some more about runetime

This commit is contained in:
2019-10-30 00:19:56 -07:00
parent 7812b1064b
commit 4b5e2f4454
2 changed files with 114 additions and 2 deletions

View File

@@ -48,7 +48,7 @@ implementation of the G-machine compilation.
We can start working on an implementation of the runtime right now,
beginning with the nodes:
{{< codelines "C++" "compiler/07/runtime.c" 5 46 >}}
{{< codelines "C++" "compiler/07/runtime.c" 5 51 >}}
We have a variety of different nodes that can be on the stack, but without
the magic of C++'s `vtable` and RTTI, we have to take care of the bookkeeping
@@ -66,3 +66,40 @@ to be any node. We do this because we sometimes mutate nodes (replacing
expressions with the results of their evaluation), changing their type.
We then want to be able to change a node without reallocating memory.
Since the biggest node we have is `node_app`, that's the one we choose.
We now move on to implement some stack operations. Let's list them off:
* `stack_init` and `stack_free` - one allocates memory for the stack,
the other releases it.
* `stack_push`, `stack_pop` and `stack_peek` - the classic stack operations.
We have `_peek` to take an offset, so we can peek relative to the top of the stack.
* `stack_popn` - pop off some number of nodes instead of one.
* `stack_slide` - the slide we specified in the semantics. Keeps the top, deletes the
next several nodes.
* `stack_update` - turns the node at the offset into an indirection to the result,
which we will use for lazy evaluation (modifying expressions with their reduced forms).
* `stack_alloc` - allocate indirection nodes on the stack. We will use this later.
Here's the implementation:
{{< codelines "C++" "compiler/07/runtime.c" 53 113 >}}
Let's not talk about how this will connect to the code we generate. To get
a quick example, consider the `node_global` struct that we have declared above.
It has a member `function`, which is a __function pointer__ to a function
that takes a stack and returns void.
When we finally generate machine code for each of the functions
we have in our program, it will be made up of sequences of G-machine
operations expressed using assembly instructions. These instructions will still
have to manipulate the G-machine stack (they still represent G-machine operations!),
and thus, the resulting assembly subroutine will take as parameter a stack. It will
then construct the function's graph on that stack, as we've already seen. Thus,
we express a compiled top-level function as a subroutine that takes a stack,
and returns void. A global node holds in it the pointer to the function that it will call.
When our program will start, it will assume that there exists a top-level
function `main` that takes 0 parameters. It will take that function, call it
to produce the initial graph, and then let the unwind loop take care of the evaluation.
Thus, our program will initially look like this:
{{< codelines "C++" "compiler/07/runtime.c" 117 125 >}}