jitters: vm: add bytecode compiler & interpreter
This commit is contained in:
parent
7c6a996499
commit
bd7180cea4
|
@ -13,6 +13,7 @@
|
||||||
#include "jit/jitter.h"
|
#include "jit/jitter.h"
|
||||||
#include "parse/parse-jitters.h"
|
#include "parse/parse-jitters.h"
|
||||||
#include "print/printer.h"
|
#include "print/printer.h"
|
||||||
|
#include "vm/vm.h"
|
||||||
|
|
||||||
const char *argp_program_version = PACKAGE_STRING;
|
const char *argp_program_version = PACKAGE_STRING;
|
||||||
const char *argp_program_bug_address = "<" PACKAGE_BUGREPORT ">";
|
const char *argp_program_bug_address = "<" PACKAGE_BUGREPORT ">";
|
||||||
|
@ -23,6 +24,7 @@ static char doc[] =
|
||||||
|
|
||||||
static struct argp_option options[] = {
|
static struct argp_option options[] = {
|
||||||
{"evaluate", 'e', 0, 0, "Evaluate input by walking the tree", 0 },
|
{"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 },
|
{"compile", 'c', 0, 0, "Compile input to assembly", 0 },
|
||||||
{"jit", 'j', 0, 0, "JIT-compile input and evaluate", 0 },
|
{"jit", 'j', 0, 0, "JIT-compile input and evaluate", 0 },
|
||||||
{"print", 'p', 0, 0, "Print parsed expression", 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 debug; // Whether to activate Bison using debug trace
|
||||||
bool compile; // Whether to compile the input
|
bool compile; // Whether to compile the input
|
||||||
bool jit; // Whether to JIT the input
|
bool jit; // Whether to JIT the input
|
||||||
|
bool virtual; // Whether to run on VM
|
||||||
bool evaluate; // Whether to evaluate the input
|
bool evaluate; // Whether to evaluate the input
|
||||||
bool print; // Whether to print the input
|
bool print; // Whether to print the input
|
||||||
const char *output_file; // Where to output
|
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':
|
case 'o':
|
||||||
arguments->output_file = arg;
|
arguments->output_file = arg;
|
||||||
break;
|
break;
|
||||||
|
case 'v':
|
||||||
|
arguments->virtual = true;
|
||||||
|
break;
|
||||||
case ARGP_KEY_ARG:
|
case ARGP_KEY_ARG:
|
||||||
argp_usage(state);
|
argp_usage(state);
|
||||||
break;
|
break;
|
||||||
|
@ -101,6 +107,8 @@ int main(int argc, char *argv[])
|
||||||
fprintf(output, "%d\n", jit_eval_ast(ast));
|
fprintf(output, "%d\n", jit_eval_ast(ast));
|
||||||
if (arguments.evaluate)
|
if (arguments.evaluate)
|
||||||
fprintf(output, "%d\n", evaluate_ast(ast));
|
fprintf(output, "%d\n", evaluate_ast(ast));
|
||||||
|
if (arguments.virtual)
|
||||||
|
fprintf(output, "%d\n", bytecompile_eval_ast(ast));
|
||||||
if (arguments.print)
|
if (arguments.print)
|
||||||
print_ast(ast, output);
|
print_ast(ast, output);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,3 +20,4 @@ include %D%/eval/local.am
|
||||||
include %D%/jit/local.am
|
include %D%/jit/local.am
|
||||||
include %D%/parse/local.am
|
include %D%/parse/local.am
|
||||||
include %D%/print/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