Write some more about runetime
This commit is contained in:
parent
7812b1064b
commit
4b5e2f4454
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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 >}}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user