Write some more about runetime

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

View File

@ -29,6 +29,7 @@ struct node_num {
struct node_global { struct node_global {
struct node_base base; struct node_base base;
int32_t arity;
void (*function)(struct stack*); void (*function)(struct stack*);
}; };
@ -44,7 +45,81 @@ struct node_data {
}; };
struct node_base* alloc_node() { struct node_base* alloc_node() {
node_base* new_node = malloc(sizeof(struct node_app)); struct node_base* new_node = malloc(sizeof(struct node_app));
assert(new_node != NULL); assert(new_node != NULL);
return new_node; return new_node;
} }
struct stack {
size_t size;
size_t count;
struct node_base** data;
};
void stack_init(struct stack* s) {
s->size = 0;
s->count = 0;
s->data = malloc(sizeof(*s->data) * s->size);
assert(s->data != NULL);
}
void stack_free(struct stack* s) {
free(s->data);
}
void stack_push(struct stack* s, struct node_base* n) {
while(s->count >= s->size) {
s->data = realloc(s->data, sizeof(*s->data) * (s->size *= 2));
assert(s->data != NULL);
}
s->data[s->count++] = n;
}
struct node_base* stack_pop(struct stack* s) {
assert(s->count > 0);
s->count--;
}
struct node_base* stack_peek(struct stack* s, size_t o) {
assert(s->count > o);
return s->data[s->count - o - 1];
}
void stack_popn(struct stack* s, size_t n) {
assert(s->count >= n);
s->count -= n;
}
void stack_slide(struct stack* s, size_t n) {
assert(s->count > n);
s->data[s->count - n - 1] = s->data[s->count - 1];
s->count -= n;
}
void stack_update(struct stack* s, size_t o) {
assert(s->count > o + 1);
struct node_ind* ind = (struct node_ind*) s->data[s->count - o - 2];
ind->base.tag = NODE_IND;
ind->next = s->data[s->count -= 1];
}
void stack_alloc(struct stack* s, size_t o) {
while(o--) {
struct node_ind* new_node = (struct node_ind*) alloc_node();
new_node->base.tag = NODE_IND;
new_node->next = NULL;
stack_push(s, (struct node_base*) new_node);
}
}
void eval(struct node_base* n);
extern void f_main(struct stack* s);
int main(int argc, char** argv) {
struct node_global* first_node = (struct node_global*) alloc_node();
first_node->base.tag = NODE_GLOBAL;
first_node->arity = 0;
first_node->function = f_main;
eval((struct node_base*) first_node);
}

View File

@ -48,7 +48,7 @@ implementation of the G-machine compilation.
We can start working on an implementation of the runtime right now, We can start working on an implementation of the runtime right now,
beginning with the nodes: 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 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 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. expressions with the results of their evaluation), changing their type.
We then want to be able to change a node without reallocating memory. 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. 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 >}}