From d782934f0cd529ab099434bacb10f4eda55f988a Mon Sep 17 00:00:00 2001 From: ProgramSnail Date: Sun, 20 Oct 2024 16:42:57 +0300 Subject: [PATCH] part of migration to gc --- byterun/include/builtin.h | 35 ++++ byterun/include/operations.h | 255 +++++++++++++++++++++++++ byterun/include/types.h | 120 ++++++++++++ byterun/src/interpreter.c | 357 ++--------------------------------- byterun/src/types.c | 27 +++ 5 files changed, 457 insertions(+), 337 deletions(-) create mode 100644 byterun/include/builtin.h create mode 100644 byterun/include/operations.h create mode 100644 byterun/include/types.h create mode 100644 byterun/src/types.c diff --git a/byterun/include/builtin.h b/byterun/include/builtin.h new file mode 100644 index 000000000..c7e814970 --- /dev/null +++ b/byterun/include/builtin.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include "operations.h" + +inline void f_read(struct State *s) { + int x = 0; + printf("> "); // TODO: ?? + scanf("%i", &x); + s_put_i(s, x); +} + +inline void f_write(struct State *s) { + int x = s_take_i(s); + printf("%i", x); + // put 0 ?? +} + +inline void f_length(struct State *s) { + // TODO +} + +inline void f_string(struct State *s) { + // TODO +} + +inline void f_array(struct State *s, int sz) { + // TODO +} + +inline void f_cons(struct State *s) { + + // TODO +} diff --git a/byterun/include/operations.h b/byterun/include/operations.h new file mode 100644 index 000000000..ab296e575 --- /dev/null +++ b/byterun/include/operations.h @@ -0,0 +1,255 @@ +#pragma once + +#include "../../runtime/runtime.h" +#include "types.h" + +#include + +// TODO: use gc + +// ------ general ------ + +// TODO: use gc +inline void free_var_ptr(union VarT *var); + +inline void free_var(union VarT var) { + switch (dh_type(var.nil.data_header)) { + case NIL_T: + break; + case INT_T: + break; + case CONST_STR_T: + break; + case STR_T: + free(var.str.value); + break; + case LIST_T: + if (var.list.value != NULL) { + free_var_ptr(to_var(var.list.value)); + } + if (var.list.next != NULL) { + free_var_ptr(to_var(var.list.next)); + } + break; + case ARRAY_T: + // dh param is size + for (size_t i = 0; i < dh_param(var.array.data_header); ++i) { + free_var_ptr(to_var(var.array.values[i])); + } + free(var.array.values); + break; + case SEXP_T: + // tag is const string, no need to free + if (var.sexp.next != NULL) { + free(var.sexp.next); + } + break; + case FUN_T: + break; + } +} + +// TODO: use gc +inline void free_var_ptr(union VarT *var) { + free_var(*var); + free((void *)var); +} + +// + +inline NilT clear_var() { return NilT{.data_header = NIL_T}; } + +// usually not required, because frame is located on shared stack +inline struct Frame clear_frame() { + struct Frame frame = { + .ret_ip = NULL, + .rp = NULL, + .ret = NULL, + .params = NULL, + .locals = NULL, + .end = NULL, + }; + return frame; +} + +// TODO: not required ?? +// inline struct Var deep_copy_var(struct Var var) { +// switch (var.type) { +// case INT_T: +// break; +// case CONST_STR_T: +// break; +// case STR_T: { +// char *old_str = var.value.str_v; +// var.value.str_v = calloc(var.size + 1, sizeof(char)); +// strcpy(var.value.str_v, old_str); +// break; +// } +// case LIST_T: +// if (var.value.list_v.elem != NULL) { +// struct Var *old_elem = var.value.list_v.elem; +// var.value.list_v.elem = calloc(1, sizeof(struct Var)); +// *var.value.list_v.elem = deep_copy_var(*old_elem); +// } +// if (var.value.list_v.next != NULL) { +// struct Var *old_next = var.value.list_v.next; +// var.value.list_v.next = calloc(1, sizeof(struct Var)); +// *var.value.list_v.next = deep_copy_var(*old_next); +// } +// break; +// case ARRAY_T: { +// struct Var *old_array = var.value.array_v; +// var.value.array_v = calloc(var.size, sizeof(char)); +// for (size_t i = 0; i < var.size; ++i) { +// var.value.array_v[i] = deep_copy_var(*(old_array + i)); +// } +// break; +// } +// case FUN_T: +// break; +// case NIL_T: +// break; +// } + +// return var; +// } + +// ------ put on stack --- + +inline void s_put_var(struct State *s, struct NilT *val) { // any var + *s->vp = val; + ++s->vp; +} + +inline void s_put_nil(struct State *s) { + struct NilT *var = alloc(); + var->data_header = NIL_T; // no param + s_put_var(s, var); +} + +inline void s_putn_nil(struct State *s, size_t n) { + for (size_t i = 0; i < n; ++i) { + s_put_nil(s); + } +} + +inline void s_put_i(struct State *s, int val) { + struct IntT *var = alloc(); + var->data_header = INT_T; // no param + var->value = val; + s_put_var(s, (NilT *)var); +} + +inline void s_put_const_str(struct State *s, + const char *val) { // memory controlled externally + struct ConstStrT *var = alloc(); + var->data_header = CONST_STR_T; // no param + var->value = val; + s_put_var(s, (NilT *)var); +} + +inline void s_put_str(struct State *s, char *val) { // memory controlled by var + struct StrT *var = alloc(); + var->data_header = STR_T; // no param + var->value = val; + s_put_var(s, (NilT *)var); +} + +// TODO +inline void s_put_array(struct State *s, int sz) { // memory controlled by var + struct Var var = { + .type = ARRAY_T, + .value.array_v = calloc(sz, sizeof(struct Var)), + .size = sz, + }; + s_put_var(s, var); + + // fill array with nils ? +} + +inline void s_put_list(struct State *s, + struct NilT *first_elem) { // memory controlled by var + struct ListT *var; + var->data_header = LIST_T; // no param + var->value = first_elem; + var->next = NULL; + + s_put_var(s, (NilT *)var); + + *first_elem = clear_var(); +} + +// ------ take from stack ------ + +inline union VarT *s_take_var(struct State *s) { + if (s->vp == s->fp->end) { + failure("take: no var"); + } + --s->vp; + + union VarT *ret = *s->vp; + *s->vp = NULL; // clear top var + return ret; +} + +inline int s_take_i(struct State *s) { + union VarT *v = s_take_var(s); + if (dh_type(v->nil.data_header) != INT_T) { + failure("take int: not int"); + } + return v->int_t.value; +} + +inline void s_drop_var(struct State *s) { + if (s->vp == s->fp->end) { + failure("drop: no var"); + } + --s->vp; + free_var_ptr(*s->vp); + *s->vp = NULL; +} + +inline void s_dropn_var(struct State *s, size_t n) { + for (size_t i = 0; i < n; ++i) { + s_drop_var(s); + } +} + +// ------ functions ------ + +// TODO +inline void s_exit_f(struct State *s) { + if (s->fp == (void *)s->stack) { + failure("exit: no func"); + } + --s->fp; + s_dropn_var(s, s->vp - s->fp->locals); + // drop local var stack and locals // TODO: check +-1 + union VarT *ret = *s->vp; + --s->vp; + s_dropn_var(s, s->vp - s->fp->params); + // drop params // TODO: check +-1 + s_put_var(s, ret); + s->ip = s->fp->rp; + + // *s->fp = clear_frame(); // clear top frame +} + +// TODO +inline void s_enter_f(struct State *s, char *func_ip, size_t params_sz, + size_t locals_sz) { + struct Frame frame = { + .rp = s->ip, // ?? + .ret = s->vp, + .params = s->vp - params_sz, // TODO: check +-1 + .locals = s->vp + 1, + .end = s->vp + locals_sz + 1, // ?? + }; + + // TODO: + s_put_nil(s); // ret + s_putn_nil(s, locals_sz); // locals + s->ip = func_ip; + (*s->fp) = frame; + ++s->fp; +} diff --git a/byterun/include/types.h b/byterun/include/types.h new file mode 100644 index 000000000..0311954de --- /dev/null +++ b/byterun/include/types.h @@ -0,0 +1,120 @@ +#pragma once + +#include "parser.h" +#include + +// ------ Var ------ + +// TODO: clojures +enum Type { + NIL_T = 0x00000000, + INT_T = 0x00000001, + CONST_STR_T = 0x00000002, + STR_T = 0x00000003, + LIST_T = 0x00000004, + ARRAY_T = 0x00000005, + SEXP_T = 0x00000006, + FUN_T = 0x00000007 +}; + +struct NilT { // AnyVarT too + int32_t data_header; +}; + +struct IntT { + int32_t data_header; + int32_t value; // int value => size = 1; +}; + +struct ConstStrT { + int32_t data_header; + const char *value; +}; + +struct StrT { + int32_t data_header; + char *value; +}; + +struct ListT { + int32_t data_header; + struct NilT *value; + struct NilT *next; +}; + +struct ArrayT { + int32_t data_header; + struct NilT **values; +}; + +struct SExpT { + int32_t data_header; + const char *tag; + struct NilT *next; +}; + +struct FunT { + int32_t data_header; + char *fun_ip; +}; + +union VarT { + struct NilT nil; + struct IntT int_t; + struct ConstStrT const_str; + struct StrT str; + struct ListT list; + struct ArrayT array; + struct SExpT sexp; + struct FunT fun; +}; + +// same to TAG in runtime +inline enum Type dh_type(int data_header) { + return (Type)(data_header & 0x00000007); +} + +// same to LEN in runtime +inline int dh_param(int data_header) { return (data_header & 0xFFFFFFF8) >> 3; } + +inline union VarT *to_var(struct NilT *var) { return (union VarT *)var; } + +// ------ Frame ------ + +struct Frame { + char *rp; // ret instruction pointer + struct NilT **ret; // store returned value + struct NilT **params; // store arguments + struct NilT **locals; // store locals + struct NilT **end; // store locals +}; + +inline uint64_t frame_locals_sz(struct Frame *frame) { + return frame->locals - frame->params; +} +inline uint64_t frame_params_sz(struct Frame *frame) { + return frame->end - frame->locals; +} + +// ------ State ------ + +union StackValue { + union VarT *var; + union VarT **var_ptr; + // struct Frame frame; // TODO + char *addr; +}; + +// inline StackValue *to_sv(void *var) { return (StackValue *)var; } + +struct State { + union StackValue *stack; // vaid** + struct NilT **vp; // var pointer + struct Frame *fp; // function frame pointer + + char *ip; // instruction pointer + char *prev_ip; // prev instruction pointer +}; + +struct State init_state(bytefile *bf); +void destruct_state(struct State *state); diff --git a/byterun/src/interpreter.c b/byterun/src/interpreter.c index d38ed215b..ea6ee928d 100644 --- a/byterun/src/interpreter.c +++ b/byterun/src/interpreter.c @@ -1,338 +1,21 @@ #include "../include/interpreter.h" +#include "../include/types.h" #include "../../runtime/runtime.h" -int read_int(char** ip) { +int ip_read_int(char** ip) { *ip += sizeof(int); return *(int*)((*ip) - sizeof(int)); } -char read_byte(char** ip) { +char ip_read_byte(char** ip) { return *(*ip)++; } -char* read_string(char** ip, bytefile* bf) { - return get_string(bf, read_int(ip)); +char* ip_read_string(char** ip, bytefile* bf) { + return get_string(bf, ip_read_int(ip)); } -// - -struct Var; - -struct ListElem { - struct Var* elem; - struct Var* next; -}; - -struct Var { - enum Type { INT_T, CONST_STR_T, STR_T, LIST_T, ARRAY_T, FUN_T, NIL_T/*?*/ } type; - size_t size; - union { - int int_v; // int value => size = 1; - const char* const_str_v; // point to str in str table => size = string size (?) - char* str_v; // poitert ot string => size = string size (?) - struct ListElem list_v; // elem => size = list size - struct Var* array_v; // pointer to array of elements => size = array size - char* fun_v; // pointer to function definition => size = 1 - } value; -}; - -struct Frame { - struct Var* params; // store arguments - struct Var* ret; // store returned value - struct Var* locals; // store locals - size_t locals_sz; - size_t params_sz; - - char* rp; // ret instruction pointer -}; - // TODO: store globals in some way ?? // maybe some first vars ?? -struct State { - struct Var* vars; - struct Frame* funcs; - struct Var* vp; // var pointer - struct Frame* fp; // function pointer - - char* ip; // instruction pointer - char* prev_ip; // prev instruction pointer -}; - -struct State init_state(bytefile *bf) { - struct State state = { - .vars = calloc(1000/* TODO */, sizeof(struct Var)), - .funcs = calloc(1000/* TODO */, sizeof(struct Frame)), - .ip = bf->code_ptr, - .prev_ip = NULL, - }; - - // TODO: init vars vith NIL_T ?? - - state.vp = state.vars; - state.fp = state.funcs; - return state; -} - -void free_state(struct State* state) { - free(state->vars); - free(state->funcs); - - state->vars = NULL; - state->funcs = NULL; - state->vp = NULL; - state->fp = NULL; - state->ip = NULL; - state->prev_ip = NULL; -} - -// - -void free_var_ptr(struct Var* var); - -void free_var(struct Var var) { - switch (var.type) { - case INT_T: - break; - case CONST_STR_T: - break; - case STR_T: - free(var.value.str_v); - break; - case LIST_T: - if (var.value.list_v.elem != NULL) { - free_var_ptr(var.value.list_v.elem); - } - if (var.value.list_v.next != NULL) { - free_var_ptr(var.value.list_v.next); - } - break; - case ARRAY_T: - for (size_t i = 0; i < var.size; ++i) { - free_var(*(var.value.array_v + i)); - } - free(var.value.array_v); - break; - case FUN_T: - break; - case NIL_T: - break; - } -} - -void free_var_ptr(struct Var* var) { - free_var(*var); - free(var); -} - -// - -struct Var clear_var() { - struct Var var = { - .type = NIL_T, - .size = 0, - }; - return var; -} - -struct Frame clear_frame() { - struct Frame frame = { - .params = NULL, - .ret = NULL, - .locals = NULL, - .params_sz = 0, - .locals_sz = 0, - }; - return frame; -} - -struct Var deep_copy_var(struct Var var) { - switch (var.type) { - case INT_T: - break; - case CONST_STR_T: - break; - case STR_T: { - char* old_str = var.value.str_v; - var.value.str_v = calloc(var.size + 1, sizeof(char)); - strcpy(var.value.str_v, old_str); - break; - } - case LIST_T: - if (var.value.list_v.elem != NULL) { - struct Var* old_elem = var.value.list_v.elem; - var.value.list_v.elem = calloc(1, sizeof(struct Var)); - *var.value.list_v.elem = deep_copy_var(*old_elem); - } - if (var.value.list_v.next != NULL) { - struct Var* old_next = var.value.list_v.next; - var.value.list_v.next = calloc(1, sizeof(struct Var)); - *var.value.list_v.next = deep_copy_var(*old_next); - } - break; - case ARRAY_T: { - struct Var *old_array = var.value.array_v; - var.value.array_v = calloc(var.size, sizeof(char)); - for (size_t i = 0; i < var.size; ++i) { - var.value.array_v[i] = deep_copy_var(*(old_array + i)); - } - break; - } - case FUN_T: - break; - case NIL_T: - break; - } - - return var; -} - -// - -void s_put_var(struct State* s, struct Var val) { - *s->vp = val; - ++s->vp; -} -void s_put_nil(struct State* s) { - struct Var var = {.type = NIL_T, .size = 0}; - s_put_var(s, var); -} -void s_putn_nil(struct State* s, size_t n) { - for (size_t i = 0; i < n; ++i) { - s_put_nil(s); - } -} -void s_put_i(struct State* s, int val) { - struct Var var = {.type = INT_T, .value.int_v = val, .size = 1}; - s_put_var(s, var); -} -void s_put_const_str(struct State* s, const char* val) { // memory controlled externally - struct Var var = {.type = CONST_STR_T, .value.const_str_v = val, .size = strlen(val)}; - s_put_var(s, var); -} -void s_put_str(struct State* s, char* val) { // memory controlled by var - struct Var var = {.type = STR_T, .value.str_v = val, .size = strlen(val)}; - s_put_var(s, var); -} -void s_put_array(struct State* s, int sz) { // memory controlled by var - struct Var var = { - .type = ARRAY_T, - .value.array_v = calloc(sz, sizeof(struct Var)), - .size = sz, - }; - s_put_var(s, var); - - // fill array with nils ? -} - -void s_put_list(struct State* s, struct Var* first_elem) { // memory controlled by var - struct Var var = { - .type = LIST_T, - .value.list_v = {.elem = first_elem, .next = NULL}, - .size = 0, - }; // TODO: size ? - s_put_var(s, var); - - *first_elem = clear_var(); -} - -// - -struct Var s_take_var(struct State* s) { - if (s->vp == s->vars) { - failure("take: no var"); - } - --s->vp; - - struct Var ret = *s->vp; - *s->vp = clear_var(); // clear top var - return ret; -} - -int s_take_i(struct State* s) { - struct Var v = s_take_var(s); - if (v.type != INT_T) { - failure("take int: not int"); - } - return v.value.int_v; -} - -void s_drop_var(struct State* s) { - if (s->vp == s->vars) { - failure("drop: no var"); - } - --s->vp; - free_var(*s->vp); - - *s->vp = clear_var(); // clear top var -} - -void s_dropn_var(struct State* s, size_t n) { - for (size_t i = 0; i < n; ++i) { - s_drop_var(s); - } -} - -// - -void s_exit_f(struct State* s) { - if (s->fp == s->funcs) { - failure("exit: no func"); - } - --s->fp; - s_dropn_var(s, s->vp - s->fp->locals); // drop local var stack and locals // TODO: check +-1 - struct Var ret = *s->vp; - --s->vp; - s_dropn_var(s, s->vp - s->fp->params); // drop params // TODO: check +-1 - s_put_var(s, ret); - s->ip = s->fp->rp; - - *s->fp = clear_frame(); // clear top frame -} - -void s_enter_f(struct State* s, char* func_ip, size_t params_sz, size_t locals_sz) { - struct Frame frame = { - .params = s->vp - params_sz, // TODO: check +-1 - .ret = s->vp, - .locals = s->vp + 1, - .params_sz = params_sz, - .locals_sz = locals_sz, - .rp = s->ip, - }; - s_put_nil(s); // ret - s_putn_nil(s, locals_sz); // locals - s->ip = func_ip; - (*s->fp) = frame; - ++s->fp; -} - -// - -// --- builtin calls -void f_read(struct State* s) { - int x = 0; - printf("> "); // TODO: ?? - scanf("%i", &x); - s_put_i(s, x); -} - -void f_write(struct State* s) { - int x = s_take_i(s); - printf("%i", x); - // put 0 ?? -} -void f_length(struct State* s) { - // TODO -} -void f_string(struct State* s) { - // TODO -} -void f_array(struct State* s, int sz) { - // TODO -} -void f_cons(struct State* s) { - - // TODO -} -// void run(bytefile *bf) { struct State s = init_state(bf); @@ -343,7 +26,7 @@ void run(bytefile *bf) { do { char* before_op_ip = s.ip; // save to set s.prev_ip - char x = read_byte(&s.ip), + char x = ip_read_byte(&s.ip), h = (x & 0xF0) >> 4, l = x & 0x0F; @@ -361,11 +44,11 @@ void run(bytefile *bf) { case 1: switch (l) { case 0: // CONST %d - s_put_i(&s, read_int(&s.ip)); + s_put_i(&s, ip_read_int(&s.ip)); break; case 1: // STRING %s - s_put_const_str(&s, read_string(&s.ip, bf)); + s_put_const_str(&s, ip_read_string(&s.ip, bf)); break; case 2: // SEXP %s %d @@ -381,7 +64,7 @@ void run(bytefile *bf) { break; case 5: // JMP 0x%.8x - s.ip = (char*)(long)read_int(&s.ip); // TODO: check + s.ip = (char*)(long)ip_read_int(&s.ip); // TODO: check break; case 6: // END @@ -452,7 +135,7 @@ void run(bytefile *bf) { switch (l) { case 0: { // CJMPz 0x%.8x // FIXME: TODO: jump by top stack condition ?? - char* new_ip = (char*)(long)read_int(&s.ip); // TODO: check + char* new_ip = (char*)(long)ip_read_int(&s.ip); // TODO: check if (s_take_i(&s) != 0) { // FIXME: bools ??, other vars ?? s.ip = new_ip; } @@ -460,27 +143,27 @@ void run(bytefile *bf) { } case 1: { // CJMPnz 0x%.8x // FIXME: TODO: jump by top stack condition ??i - char* new_ip = (char*)(long)read_int(&s.ip); // TODO: check + char* new_ip = (char*)(long)ip_read_int(&s.ip); // TODO: check if (s_take_i(&s) == 0) { // FIXME: bools ??, other vars ?? s.ip = new_ip; } break; } case 2: // BEGIN %d %d - s_enter_f(&s, s.prev_ip/*ip from call*/, read_int(&s.ip), read_int(&s.ip)); + s_enter_f(&s, s.prev_ip/*ip from call*/, ip_read_int(&s.ip), ip_read_int(&s.ip)); // TODO: is func enter ? break; case 3: // CBEGIN %d %d - s_enter_f(&s, s.prev_ip/*ip from call*/, read_int(&s.ip), read_int(&s.ip)); + s_enter_f(&s, s.prev_ip/*ip from call*/, ip_read_int(&s.ip), ip_read_int(&s.ip)); // TODO: is func enter ? break; case 4: // CLOSURE 0x%.8x { - int n = read_int(&s.ip); + int n = ip_read_int(&s.ip); for (int i = 0; i < n; i++) { - switch (read_byte(&s.ip)) { + switch (ip_read_byte(&s.ip)) { // case 0: // G(%d) // case 1: // L(%d) // case 2: // A(%d) @@ -495,13 +178,13 @@ void run(bytefile *bf) { case 5: // CALLC %d // TODO: no arguments given ?? // TODO: jump only ?? - s.ip = (char*)(long)read_int(&s.ip); // TODO: check + s.ip = (char*)(long)ip_read_int(&s.ip); // TODO: check break; case 6: // CALL 0x%.8x %d // TODO: second arg is given params amount ?? // TODO: jump only ?? - s.ip = (char*)(long)read_int(&s.ip); // TODO: check + s.ip = (char*)(long)ip_read_int(&s.ip); // TODO: check break; case 7: // TAG %s @@ -509,12 +192,12 @@ void run(bytefile *bf) { break; case 8: // ARRAY %d - s_put_array(&s, read_int(&s.ip)); + s_put_array(&s, ip_read_int(&s.ip)); break; case 9: // FAIL %d %d // TODO: ?? - failure("FAIL: %d-%d\n", read_int(&s.ip), read_int(&s.ip)); + failure("FAIL: %d-%d\n", ip_read_int(&s.ip), ip_read_int(&s.ip)); break; case 10: // LINE %d @@ -549,7 +232,7 @@ void run(bytefile *bf) { break; case 4: // CALL Barray %d - f_array(&s, read_int(&s.ip)); + f_array(&s, ip_read_int(&s.ip)); break; default: diff --git a/byterun/src/types.c b/byterun/src/types.c new file mode 100644 index 000000000..c00df02fd --- /dev/null +++ b/byterun/src/types.c @@ -0,0 +1,27 @@ +#include "../include/types.h" + +#include + +// TODO: gc use + +struct State init_state(bytefile *bf) { + struct State state = { + .stack = calloc(1000/* TODO */, sizeof(void*)), + .ip = bf->code_ptr, + .prev_ip = NULL, + }; + + state.vp = *state.stack; + state.fp = NULL; + return state; +} + +void destruct_state(struct State* state) { + free(state->stack); + + state->vp = NULL; + state->fp = NULL; + state->ip = NULL; + state->prev_ip = NULL; +} +