jitters: vm: add bytecode compiler & interpreter

This commit is contained in:
Bruno BELANYI 2020-09-30 22:35:34 +02:00
parent 7c6a996499
commit bd7180cea4
7 changed files with 262 additions and 0 deletions

View file

@ -13,6 +13,7 @@
#include "jit/jitter.h"
#include "parse/parse-jitters.h"
#include "print/printer.h"
#include "vm/vm.h"
const char *argp_program_version = PACKAGE_STRING;
const char *argp_program_bug_address = "<" PACKAGE_BUGREPORT ">";
@ -23,6 +24,7 @@ static char doc[] =
static struct argp_option options[] = {
{"evaluate", 'e', 0, 0, "Evaluate input by walking the tree", 0 },
{"virtual", 'v', 0, 0, "Compile input bytecode and interpret", 0 },
{"compile", 'c', 0, 0, "Compile input to assembly", 0 },
{"jit", 'j', 0, 0, "JIT-compile input and evaluate", 0 },
{"print", 'p', 0, 0, "Print parsed expression", 0 },
@ -36,6 +38,7 @@ struct arguments
bool debug; // Whether to activate Bison using debug trace
bool compile; // Whether to compile the input
bool jit; // Whether to JIT the input
bool virtual; // Whether to run on VM
bool evaluate; // Whether to evaluate the input
bool print; // Whether to print the input
const char *output_file; // Where to output
@ -65,6 +68,9 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
case 'o':
arguments->output_file = arg;
break;
case 'v':
arguments->virtual = true;
break;
case ARGP_KEY_ARG:
argp_usage(state);
break;
@ -101,6 +107,8 @@ int main(int argc, char *argv[])
fprintf(output, "%d\n", jit_eval_ast(ast));
if (arguments.evaluate)
fprintf(output, "%d\n", evaluate_ast(ast));
if (arguments.virtual)
fprintf(output, "%d\n", bytecompile_eval_ast(ast));
if (arguments.print)
print_ast(ast, output);
}

View file

@ -20,3 +20,4 @@ include %D%/eval/local.am
include %D%/jit/local.am
include %D%/parse/local.am
include %D%/print/local.am
include %D%/vm/local.am

102
src/vm/bytecode.c Normal file
View file

@ -0,0 +1,102 @@
#include "bytecode.h"
#include <err.h>
#include <stdlib.h>
#include <string.h>
static void *xmalloc(size_t size)
{
void *ret = malloc(size);
if (ret == NULL)
err(1, NULL);
return ret;
}
static void *xrealloc(void *ptr, size_t size)
{
void *ret = realloc(ptr, size);
if (ret == NULL)
{
free(ptr);
err(1, NULL);
}
return ret;
}
struct stack *init_stack(void)
{
struct stack *ret = xmalloc(sizeof(*ret));
size_t init_cap = sizeof(struct bytecode_num);
ret->buf = xmalloc(init_cap);
ret->capacity = init_cap;
ret->size = 0;
return ret;
}
void destroy_stack(struct stack *stack)
{
free(stack->buf);
free(stack);
}
static void resize_stack(struct stack *stack)
{
stack->buf = xrealloc(stack->buf, stack->capacity * 2);
void *old_op = get_op(stack);
stack->capacity *= 2;
void *new_op = get_op(stack);
memmove(new_op, old_op, stack->size);
}
static void *get_end_op(struct stack *stack)
{
char *tmp = stack->buf;
return tmp + stack->capacity - stack->size - sizeof(struct bytecode);
}
static void *get_end_num(struct stack *stack)
{
char *tmp = stack->buf;
return tmp + stack->capacity - stack->size - sizeof(struct bytecode_num);
}
void push_op(struct stack *stack, enum bytecode_kind kind)
{
if (stack->capacity - stack->size < sizeof(struct bytecode))
resize_stack(stack);
struct bytecode *op = get_end_op(stack);
op->kind = kind;
stack->size += sizeof(*op);
}
void push_num(struct stack *stack, int val)
{
if (stack->capacity - stack->size < sizeof(struct bytecode_num))
resize_stack(stack);
struct bytecode_num *num = get_end_num(stack);
num->header.kind = NUM_CODE;
num->num = val;
stack->size += sizeof(*num);
}
struct bytecode *get_op(const struct stack *stack)
{
char *tmp = stack->buf;
void *op = tmp + stack->capacity - stack->size;
return op;
}

37
src/vm/bytecode.h Normal file
View file

@ -0,0 +1,37 @@
#ifndef BYTECODE_H
#define BYTECODE_H
#include <stddef.h>
struct bytecode
{
enum bytecode_kind
{
NUM_CODE,
PLUS_CODE,
MINUS_CODE,
TIMES_CODE,
DIVIDE_CODE,
} kind;
};
struct bytecode_num
{
struct bytecode header;
int num;
};
struct stack
{
void *buf;
size_t size;
size_t capacity;
};
struct stack *init_stack(void);
void destroy_stack(struct stack *stack);
void push_op(struct stack *stack, enum bytecode_kind kind);
void push_num(struct stack *stack, int val);
struct bytecode *get_op(const struct stack *stack);
#endif /* !BYTECODE_H */

14
src/vm/local.am Normal file
View file

@ -0,0 +1,14 @@
jitters_LDADD += \
libvm.a \
$(NULL)
noinst_LIBRARIES += \
libvm.a \
$(NULL)
libvm_a_SOURCES = \
%D%/bytecode.c \
%D%/bytecode.h \
%D%/vm.c \
%D%/vm.h \
$(NULL)

92
src/vm/vm.c Normal file
View file

@ -0,0 +1,92 @@
#include "vm.h"
#include "bytecode.h"
static enum bytecode_kind binop_to_code(enum binop_kind op)
{
switch (op)
{
case PLUS:
return PLUS_CODE;
case MINUS:
return MINUS_CODE;
case TIMES:
return TIMES_CODE;
case DIVIDE:
return DIVIDE_CODE;
}
return PLUS_CODE; // To avoid errors...
}
static void bytecompile(struct stack *stack, const struct ast_node *ast)
{
switch (ast->kind)
{
case NUM:
push_num(stack, ast->val.num);
break;
case UNOP:
if (ast->val.un_op.op == IDENTITY)
bytecompile(stack, ast->val.un_op.rhs);
else
{
bytecompile(stack, ast->val.un_op.rhs);
push_num(stack, 0);
push_op(stack, MINUS_CODE);
}
break;
case BINOP:
bytecompile(stack, ast->val.bin_op.rhs);
bytecompile(stack, ast->val.bin_op.lhs);
push_op(stack, binop_to_code(ast->val.bin_op.op));
break;
}
}
static int get_num(const void *op)
{
const struct bytecode_num *num = op;
return num->num;
}
static int interpret(struct stack *stack)
{
const struct bytecode *op = get_op(stack);
switch (op->kind)
{
case NUM_CODE:
stack->size -= sizeof(struct bytecode_num);
return get_num(op);
default:
break;
}
stack->size -= sizeof(struct bytecode);
int lhs = interpret(stack);
int rhs = interpret(stack);
switch (op->kind)
{
case PLUS_CODE:
return lhs + rhs;
case MINUS_CODE:
return lhs - rhs;
case TIMES_CODE:
return lhs * rhs;
case DIVIDE_CODE:
return lhs / rhs;
default:
return 0; // Should not happen
}
}
int bytecompile_eval_ast(const struct ast_node *ast)
{
struct stack *stack = init_stack();
bytecompile(stack, ast);
int ret = interpret(stack);
destroy_stack(stack);
return ret;
}

8
src/vm/vm.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef VM_H
#define VM_H
#include "ast/ast.h"
int bytecompile_eval_ast(const struct ast_node *ast);
#endif /* !VM_H */