diff --git a/byterun/include/stack.h b/byterun/include/stack.h index fd1187da0..c63ef6e3c 100644 --- a/byterun/include/stack.h +++ b/byterun/include/stack.h @@ -29,8 +29,11 @@ void s_popn(struct State *s, size_t n); // // where |> defines corresponding frame pointer, | is stack pointer // location before / after new frame added -void s_enter_f(struct State *s, char *rp, auint args_sz, auint locals_sz); +void s_enter_f(struct State *s, char *rp, bool is_closure_call, auint args_sz, + auint locals_sz); void s_exit_f(struct State *s); +// ---- category --- + void **var_by_category(struct State *s, enum VarCategory category, int id); diff --git a/byterun/include/types.h b/byterun/include/types.h index a8162b244..d16e0111a 100644 --- a/byterun/include/types.h +++ b/byterun/include/types.h @@ -3,6 +3,7 @@ #include "../../runtime/runtime.h" #include "../../runtime/runtime_common.h" #include "parser.h" +#include #include // ------ General ------ @@ -21,6 +22,7 @@ static const size_t MAX_ARRAY_SIZE = 0x11111110; // ------ Frame ------ struct Frame { + void *closure; // where closure value stored if needed void *ret; // store returned value [gc pointer] char *rp; // ret instruction pointer [not gc pointer] void **prev_fp; // ret function frame pointer [boxed value, not gc @@ -39,16 +41,18 @@ void **f_args(struct Frame *fp); // ------ State ------ struct State { - void **stack; + void *stack[STACK_SIZE + 1]; void **sp; // stack pointer struct Frame *fp; // function frame pointer bytefile *bf; + bool is_closure_call; + char *ip; // instruction pointer char *call_ip; // prev instruction pointer (to remember jmp locations) }; -struct State init_state(bytefile *bf); +void init_state(bytefile *bf, struct State *s); void cleanup_state(struct State *state); // ------ VarCategory ------ @@ -57,7 +61,7 @@ enum VarCategory { VAR_GLOBAL = 0, VAR_LOCAL = 1, VAR_ARGUMENT = 2, - VAR_C = 3 // TODO: constants ?? + VAR_CLOSURE = 3 }; enum VarCategory to_var_category(uint8_t category); diff --git a/byterun/src/interpreter.c b/byterun/src/interpreter.c index 563db1aa3..c961aa3ec 100644 --- a/byterun/src/interpreter.c +++ b/byterun/src/interpreter.c @@ -19,10 +19,9 @@ char *ip_read_string(char **ip, bytefile *bf) { return get_string(bf, ip_read_int(ip)); } -// TODO: store globals in some way ?? // maybe some first vars ?? - void run(bytefile *bf) { - struct State s = init_state(bf); + struct State s; + init_state(bf, &s); print_stack(&s); printf("--- interpreter run ---\n"); @@ -59,6 +58,7 @@ void run(bytefile *bf) { do { // char *before_op_ip = s.ip; // save to set s.prev_ip + bool call_happened = false; char x = ip_read_byte(&s.ip), h = (x & 0xF0) >> 4, l = x & 0x0F; @@ -93,20 +93,45 @@ void run(bytefile *bf) { break; } - case 2: // SEXP %s %d // create sexpr with tag=%s and %d elements from - // stack + case 2: { // SEXP %s %d // create sexpr with tag=%s and %d elements from + // stack // params read from stack - s_push_i(&s, LtagHash(ip_read_string(&s.ip, bf))); - Bsexp((aint *)s.sp, ip_read_int(&s.ip)); // TODO: check order + const char* name = ip_read_string(&s.ip, bf); + aint args_count = ip_read_int(&s.ip); + printf("tag hash is %i, n os %i\n", UNBOX(LtagHash((char*)name)), args_count); + + if (args_count < 0) { + failure("SEXP: args count should be >= 0"); + } + void ** buffer = calloc(args_count + 1, sizeof(void*)); + + for (size_t i = 0; i < args_count; ++i) { + buffer[i] = s_pop(&s); + } + buffer[args_count] = (void*)LtagHash((char*)name); + + // TODO: FIXME: not working with elems + void* sexp = Bsexp((aint *)buffer, BOX(args_count + 1)); + + push_extra_root(sexp); + s_push(&s, sexp); + pop_extra_root(sexp); + + free(buffer); break; + } case 3: // STI - write by ref (?) // TODO break; - case 4: // STA - write to array elem - // Bsta // TODO + case 4: { // STA - write to array elem + void* elem = s_pop(&s); + aint index = s_pop_i(&s); + void* data = s_pop(&s); + s_push(&s, Bsta(data, index, elem)); break; + } case 5: { // JMP 0x%.8x int jmp_p = ip_read_int(&s.ip); // TODO: check @@ -140,12 +165,7 @@ void run(bytefile *bf) { case 9: // DUP { - if (s.sp == s.stack + STACK_SIZE || - (s.fp != NULL && s.sp == f_locals(s.fp))) { - failure("can't DUP: no value on stack"); - } - *s.sp = *(s.sp - 1); - ++s.sp; + s_push(&s, *s.sp); break; } @@ -164,9 +184,9 @@ void run(bytefile *bf) { case 11: // ELEM { - void *array = s_pop(&s); aint index = s_pop_i(&s); - s_push(&s, Belem(array, index)); + void *data = s_pop(&s); + s_push(&s, Belem(data, index)); } break; default: @@ -223,43 +243,62 @@ void run(bytefile *bf) { case 2: { // BEGIN %d %d // function begin int args_sz = ip_read_int(&s.ip); int locals_sz = ip_read_int(&s.ip); - s_enter_f(&s, s.call_ip /*ip from call*/, args_sz, locals_sz); + if (s.fp != NULL && s.call_ip == NULL) { + failure("BEGIN: not after call"); + } + s_enter_f(&s, s.call_ip /*ip from call*/, s.is_closure_call, args_sz, locals_sz); break; } - case 3: { // CBEGIN %d %d // TODO: clojure begin ?? + case 3: { // CBEGIN %d %d // TODO: used ?? int args_sz = ip_read_int(&s.ip); int locals_sz = ip_read_int(&s.ip); - s_enter_f(&s, s.call_ip /*ip from call*/, args_sz, locals_sz); + if (s.fp != NULL && s.call_ip == NULL) { + failure("BEGIN: not after call"); + } + s_enter_f(&s, s.call_ip /*ip from call*/, s.is_closure_call, args_sz, locals_sz); break; } - case 4: // CLOSURE 0x%.8x - // TODO + case 4: // CLOSURE 0x%.8x // TODO: check { - int n = ip_read_int(&s.ip); - for (int i = 0; i < n; i++) { - switch (ip_read_byte(&s.ip)) { - // case 0: // G(%d) - // case 1: // L(%d) - // case 2: // A(%d) - // case 3: // C(%d) - default: - failure("invalid opcode %d-%d\n", h, l); - } + aint call_offset = ip_read_int(&s.ip); + aint args_count = ip_read_int(&s.ip); + for (aint i = 0; i < args_count; i++) { + aint arg_type = ip_read_byte(&s.ip); + aint arg_id = ip_read_int(&s.ip); + + void **var_ptr = + var_by_category(&s, to_var_category(l), ip_read_int(&s.ip)); + s_push(&s, *var_ptr); } - }; + s_push(&s, bf->code_ptr + call_offset); // TODO: check way of storage ?? + + void* closure = Bclosure((aint*)s.sp, args_count); + + push_extra_root(closure); + s_popn(&s, args_count + 1); + s_push(&s, closure); + pop_extra_root(closure); break; + } case 5: // CALLC %d // call clojure - // TODO FIXME: call clojure - // s.ip = (char*)(long)ip_read_int(&s.ip); // TODO: check + ip_read_int(&s.ip); // args count + + call_happened = true; + s.is_closure_call = true; + s.call_ip = s.ip; + + s.ip = Belem(s.sp, BOX(0)); // use offset instead ?? break; case 6: { // CALL 0x%.8x %d // call function - int call_p = ip_read_int(&s.ip); // TODO: check - ip_read_int(&s.ip); + int call_p = ip_read_int(&s.ip); + ip_read_int(&s.ip); // args count + call_happened = true; + s.is_closure_call = false; s.call_ip = s.ip; if (call_p < 0) { @@ -269,9 +308,22 @@ void run(bytefile *bf) { break; } - case 7: // TAG %s - s_push_i(&s, LtagHash(ip_read_string(&s.ip, bf))); // TODO: check + case 7: { // TAG %s %d + const char* name = ip_read_string(&s.ip, bf); + aint args_count = ip_read_int(&s.ip); + + printf("tag hash is %i, n is %i, peek is %i\n", UNBOX(LtagHash((char*)name)), args_count, s_peek(&s)); + if (UNBOXED(s_peek(&s))) printf("aaaaaaaaaaaaaaaaaaa"); + else { + data* r = TO_DATA(s_peek(&s)); + if ((aint)BOX(TAG(r->data_header) != SEXP_TAG)) printf("bbbbbbbb %i %i", TAG(r->data_header), SEXP_TAG); + if (TO_SEXP(s_peek(&s))->tag != UNBOX(LtagHash((char*)name))) printf("cccccccccc"); + if (LEN(r->data_header) != UNBOX(args_count)) printf("dddddd %i %i", LEN(r->data_header), UNBOX(args_count)); + } + + s_push_i(&s, Btag(s_pop(&s), LtagHash((char*)name), args_count)); break; + } case 8: // ARRAY %d Barray((aint *)s.sp, ip_read_int(&s.ip)); @@ -291,11 +343,11 @@ void run(bytefile *bf) { } break; - case 6: // PATT pats[l] // TODO: check + case 6: // PATT pats[l] // {"=str", "#string", "#array", "#sexp", "#ref", "#val", "#fun"} switch (l) { - case 0: // =str - s_push_i(&s, Bstring_patt(s_pop(&s), s_pop(&s))); // TODO: order + case 0: // =str + s_push_i(&s, Bstring_patt(s_pop(&s), s_pop(&s))); break; case 1: // #string s_push_i(&s, Bstring_tag_patt(s_pop(&s))); @@ -307,10 +359,11 @@ void run(bytefile *bf) { s_push_i(&s, Bsexp_tag_patt(s_pop(&s))); break; case 4: // #ref + s_push_i(&s, Bunboxed_patt(s_pop(&s))); // TODO: check // TODO break; case 5: // #val - // TODO + s_push_i(&s, Bboxed_patt(s_pop(&s))); // TODO: check break; case 6: // #fun s_push_i(&s, Bclosure_tag_patt(s_pop(&s))); @@ -359,7 +412,10 @@ void run(bytefile *bf) { failure("invalid opcode %d-%d\n", h, l); } - // s.call_ip = before_op_ip; + if (!call_happened) { + s.is_closure_call = false; + s.call_ip = NULL; + } if (s.fp == NULL) { break; diff --git a/byterun/src/stack.c b/byterun/src/stack.c index 90bf70b6c..bf428ed91 100644 --- a/byterun/src/stack.c +++ b/byterun/src/stack.c @@ -4,6 +4,34 @@ extern size_t __gc_stack_top, __gc_stack_bottom; +#define PRE_GC() \ + bool flag = false; \ + flag = __gc_stack_top == 0; \ + if (flag) { __gc_stack_top = (size_t)__builtin_frame_address(0); } \ + assert(__gc_stack_top != 0); \ + assert((__gc_stack_top & 0xF) == 0); \ + assert(__builtin_frame_address(0) <= (void *)__gc_stack_top); + +#define POST_GC() \ + assert(__builtin_frame_address(0) <= (void *)__gc_stack_top); \ + if (flag) { __gc_stack_top = 0; } + +// ------ + +#define ASSERT_BOXED(memo, x) \ + do \ + if (UNBOXED(x)) failure("boxed value expected in %s\n", memo); \ + while (0) +#define ASSERT_UNBOXED(memo, x) \ + do \ + if (!UNBOXED(x)) failure("unboxed value expected in %s\n", memo); \ + while (0) +#define ASSERT_STRING(memo, x) \ + do \ + if (!UNBOXED(x) && TAG(TO_DATA(x)->data_header) != STRING_TAG) \ + failure("string value expected in %s\n", memo); \ + while (0) + // ------ basic stack oprs ------ void** s_top(struct State* s) { @@ -82,7 +110,7 @@ void s_popn(struct State *s, size_t n) { // ------ functions ------ -void s_enter_f(struct State *s, char *rp, auint args_sz, auint locals_sz) { +void s_enter_f(struct State *s, char *rp, bool is_closure_call, auint args_sz, auint locals_sz) { printf("-> %i args sz\n", args_sz); printf("-> %i locals sz\n", locals_sz); @@ -94,11 +122,18 @@ void s_enter_f(struct State *s, char *rp, auint args_sz, auint locals_sz) { failure("not enough parameters in function stack"); } + if (!is_closure_call) { + s_push_nil(s); + } + + void* closure = s_peek(s); + // s_push_nil(s); // sp contains value, frame starts with next value - s_pushn_nil(s, frame_sz()); + s_pushn_nil(s, frame_sz() - 1); // create frame struct Frame frame = { + .closure = closure, .ret = NULL, // field in frame itself .rp = rp, .prev_fp = (void**)s->fp, @@ -149,6 +184,8 @@ void print_stack(struct State* s) { printf("]\n"); } +// --- category --- + void **var_by_category(struct State *s, enum VarCategory category, int id) { if (id < 0) { @@ -169,7 +206,7 @@ void **var_by_category(struct State *s, enum VarCategory category, if (f_locals_sz(s->fp) <= id) { failure("can't read local: too big id, %i >= %ul", id, f_locals_sz(s->fp)); } - printf("id is %i, local is %i, %i\n", id, UNBOX((auint)*((void**)f_locals(s->fp) + id)), f_locals(s->fp) - s->sp); + // printf("id is %i, local is %i, %i\n", id, UNBOX((auint)*((void**)f_locals(s->fp) + id)), f_locals(s->fp) - s->sp); var = f_locals(s->fp) + (f_locals_sz(s->fp) - id - 1); break; case VAR_ARGUMENT: @@ -179,11 +216,26 @@ void **var_by_category(struct State *s, enum VarCategory category, if (f_args_sz(s->fp) <= id) { failure("can't read arguments: too big id, %i >= %ul", id, f_args_sz(s->fp)); } - printf("id is %i, arg is %i, %i\n", id, UNBOX((auint)*((void**)f_args(s->fp) + id)), f_args(s->fp) - s->sp); - var = f_args(s->fp) + (f_args_sz(s->fp) - id - 1); // TODO: check if not reversed order + var = f_args(s->fp) + (f_args_sz(s->fp) - id - 1); + break; + case VAR_CLOSURE: + if (s->fp == NULL) { + failure("can't read closure parameter outside of function"); + } + if (s->fp->closure == NULL) { + failure("can't read closure parameter not in closure"); + } + if (UNBOXED(s->fp->closure)) { ASSERT_BOXED(".elem:1", s->fp->closure); } + data* d = TO_DATA(s->fp->closure); + size_t count = get_len(d) - 1; + printf("id is %i, count is %i\n", id, count); + if (count <= id) { + failure("can't read arguments: too big id, %i >= %ul", id, count); + } + // TODO: check if not reversed order + return (void **)d->contents + id; + // &Belem(s->fp->closure, BOX(id + 1)); break; - case VAR_C: // clojure ?? - // TODO: ?? break; } diff --git a/byterun/src/types.c b/byterun/src/types.c index 06054ee8f..16d2f6aeb 100644 --- a/byterun/src/types.c +++ b/byterun/src/types.c @@ -24,39 +24,36 @@ void **f_args(struct Frame *fp) { return (void **)fp + frame_sz(); } // --- State --- -static struct State alloc_state(bytefile *bf) { - struct State state = { - .stack = calloc(STACK_SIZE + 1, sizeof(void*)), - .ip = bf->code_ptr, - .call_ip = NULL, - .bf = bf, - }; +static void alloc_state(bytefile *bf, struct State* s) { + // s->stack = calloc(STACK_SIZE + 1, sizeof(void*)); + s->bf = bf; + s->is_closure_call = false; + s->ip = bf->code_ptr; + s->call_ip = NULL; for (size_t i = 0; i < STACK_SIZE; ++i) { - state.stack[i] = NULL; + s->stack[i] = NULL; } - state.sp = state.stack + STACK_SIZE; // [top -> bottom] stack - print_stack(&state); - state.fp = NULL; - return state; + s->sp = s->stack + STACK_SIZE; // [top -> bottom] stack + print_stack(s); + s->fp = NULL; } -struct State init_state(bytefile *bf) { +void init_state(bytefile *bf, struct State* s) { __init(); - struct State state = alloc_state(bf); - __gc_stack_bottom = (size_t)state.sp; - // print_stack(&state); + alloc_state(bf, s); + __gc_stack_bottom = (size_t)s->sp; + // print_stack(s); - s_pushn_nil(&state, bf->global_area_size); + s_pushn_nil(s, bf->global_area_size); // print_stack(&state); printf("- state init done\n"); - return state; } static void destruct_state(struct State* state) { - free(state->stack); + // free(state->stack); state->sp = NULL; state->fp = NULL;