jitters: vm: add bytecode compiler & interpreter
This commit is contained in:
parent
7c6a996499
commit
bd7180cea4
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
102
src/vm/bytecode.c
Normal 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
37
src/vm/bytecode.h
Normal 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
14
src/vm/local.am
Normal 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
92
src/vm/vm.c
Normal 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
8
src/vm/vm.h
Normal 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 */
|
Loading…
Reference in a new issue