From 555094b2d87f7540ce698c1beee6cbae2589392d Mon Sep 17 00:00:00 2001 From: hellerve Date: Mon, 19 Nov 2018 21:21:43 +0100 Subject: [PATCH 01/20] eval: preliminary jit --- src/eval.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/parser.c | 22 +++++----- 2 files changed, 131 insertions(+), 13 deletions(-) diff --git a/src/eval.c b/src/eval.c index 40542c0..036ed2a 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1,7 +1,10 @@ #define _DEFAULT_SOURCE #include +#include #include +#include #include +#include #include #include "eval.h" @@ -106,6 +109,121 @@ do_move_ptr: while (t[h]) h+=c.arg; DISPATCH(); #undef DISPATCH } +unsigned int relative_32bit_offset(int jump_frm, int jump_to) { + if (jump_to >= jump_frm) return jump_to - jump_frm; + else return ~((unsigned int)jump_frm-jump_to) + 1; +} + +typedef void (*jit_fn)(void); + +void* jit(void* arg) { +#define add_at(x, i) {\ + sys[i] = x;\ +} +#define add(x) {\ + sys = realloc(sys, ++syslen);\ + sys[syslen-1] = x;\ +} +#define add32_at(x, i) {\ + add_at(x & 0xff, i);\ + add_at((x >> 8) & 0xff, i+1);\ + add_at((x >> 16) & 0xff, i+2);\ + add_at((x >> 24) & 0xff, i+3);\ +} +#define add32(x) {\ + add(x & 0xff);\ + add((x >> 8) & 0xff);\ + add((x >> 16) & 0xff);\ + add((x >> 24) & 0xff);\ +} +#define add64(x) {\ + add32(x & 0xffffffff);\ + add32((x >> 32) & 0xffffffff);\ +} + int i = 0; + unsigned int t[TAPE_LEN]; + actor_ctx* ctx = (actor_ctx*) arg; + bytecode c; + unsigned int syslen = 0; + unsigned char* sys = NULL; + int loop_stack[256]; + int loop_depth = 0; + + for (int idx = 0; idx < TAPE_LEN; idx++) t[idx] = 0; + + add(0x49); + add(0xBD); + add64((uint64_t)&t); + + while (1) { + c = ctx->code[i++]; + switch (c.code) { + case INC: add(0x41); add(0x80); add(0x45); add(0x00); add(0x01); break; + case DEC: add(0x41); add(0x80); add(0x6d); add(0x00); add(0x01); break; + case FWD: add(0x49); add(0xff); add(0xc5); break; + case BCK: add(0x49); add(0xff); add(0xcd); break; + case PRN: + add(0x48); add(0xc7); add(0xc0); add(0x01); add(0x00); add(0x00); add(0x00); + add(0x48); add(0xc7); add(0xc7); add(0x01); add(0x00); add(0x00); add(0x00); + add(0x4c); add(0x89); add(0xee); + add(0x48); add(0xc7); add(0xc2); add(0x01); add(0x00); add(0x00); add(0x00); + add(0x0f); add(0x05); + break; + case READ: + add(0x48); add(0xC7); add(0xC0); add(0x00); add(0x00); add(0x00); add(0x00); + add(0x48); add(0xC7); add(0xC7); add(0x00); add(0x00); add(0x00); add(0x00); + add(0x4C); add(0x89); add(0xEE); + add(0x48); add(0xC7); add(0xC2); add(0x01); add(0x00); add(0x00); add(0x00); + add(0x0F); add(0x05); + break; + case STARTL: + add(0x41); add(0x80); add(0x7d); add(0x00); add(0x00); + + loop_stack[loop_depth++] = syslen; + add(0x0F); add(0x84); add32(0); + break; + case ENDL: { + int begin = loop_stack[--loop_depth]; + add(0x41); add(0x80); add(0x7d); add(0x00); add(0x00); + + int jmp_frm = syslen + 6; + int jmp_to = begin + 6; + int pcrel_offset = relative_32bit_offset(jmp_frm, jmp_to); + + add(0x0F); add(0x85); + add32(pcrel_offset); + + jmp_to = begin + 6; + jmp_frm = syslen; + pcrel_offset = relative_32bit_offset(jmp_frm, jmp_to); + add32_at(pcrel_offset, begin+2); + break; + } + case HALT: + default: + goto jit_start; + } + } +jit_start: + add(0xc3); + + void* mem = mmap(0, syslen, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + memcpy(mem, sys, syslen); + mprotect(mem, syslen, PROT_READ | PROT_EXEC); + + jit_fn f = (jit_fn)mem; + + f(); + + free(sys); + + return NULL; +#undef add +#undef add32 +#undef add64 +#undef add32_at +} + void eval_actors(actors* ac) { int i; pthread_t ts[ac->num]; @@ -129,12 +247,12 @@ void eval_actors(actors* ac) { ctxs[i] = ctx; if(ac->num == 1) { // if we just have one thread, we eval it directly; buys us about 2% perf - eval(&ctx); + jit(&ctx); free(ctx.up); free(ctx.up_written); return; } else { - pthread_create(&ts[i], NULL, eval, &ctxs[i]); + pthread_create(&ts[i], NULL, jit, &ctxs[i]); } down = ctx.up; down_written = ctx.up_written; diff --git a/src/parser.c b/src/parser.c index 7cf298b..1e94c21 100644 --- a/src/parser.c +++ b/src/parser.c @@ -89,7 +89,7 @@ actors* parse(char* inpt) { ac->code = NULL; bytecode* res = NULL; inpt = remove_comments(inpt); - inpt = optimize_zero(inpt); + //inpt = optimize_zero(inpt); while (*inpt != '\0') { switch (*inpt) { @@ -114,16 +114,16 @@ actors* parse(char* inpt) { } build_op(ENDL, matching_begin+1); - int removed = optimize_loop(res, matching_begin+1, idx); - idx -= removed; - dup += removed; - res = realloc(res, sizeof(bytecode)*idx); - if (!removed) { - res[matching_begin].arg = idx; - if (res[matching_begin].code != STARTL && idx < 30) { - printf("wat: %d %d\n", matching_begin, idx); - } - } + //int removed = optimize_loop(res, matching_begin+1, idx); + //idx -= removed; + //dup += removed; + //res = realloc(res, sizeof(bytecode)*idx); + //if (!removed) { + // res[matching_begin].arg = idx; + // if (res[matching_begin].code != STARTL && idx < 30) { + // printf("wat: %d %d\n", matching_begin, idx); + // } + //} break; } From 8d8085693ad9fcfb7dddafa8e7eee14b4224883f Mon Sep 17 00:00:00 2001 From: hellerve Date: Tue, 20 Nov 2018 10:49:11 +0100 Subject: [PATCH 02/20] eval: no deref of pointer --- src/eval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eval.c b/src/eval.c index 036ed2a..f8c8914 100644 --- a/src/eval.c +++ b/src/eval.c @@ -152,8 +152,8 @@ void* jit(void* arg) { for (int idx = 0; idx < TAPE_LEN; idx++) t[idx] = 0; add(0x49); - add(0xBD); - add64((uint64_t)&t); + add(0xbd); + add64((uint64_t)t); while (1) { c = ctx->code[i++]; From 5f95e87b6ccd5360bdb56510946ff66609b7c10a Mon Sep 17 00:00:00 2001 From: hellerve Date: Mon, 3 Dec 2018 12:21:42 +0100 Subject: [PATCH 03/20] eval: fix a few bugs in jit --- src/eval.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/eval.c b/src/eval.c index f8c8914..ac4bffa 100644 --- a/src/eval.c +++ b/src/eval.c @@ -125,19 +125,19 @@ void* jit(void* arg) { sys[syslen-1] = x;\ } #define add32_at(x, i) {\ - add_at(x & 0xff, i);\ - add_at((x >> 8) & 0xff, i+1);\ - add_at((x >> 16) & 0xff, i+2);\ - add_at((x >> 24) & 0xff, i+3);\ + add_at((x) & 0xff, i);\ + add_at(((x) >> 8) & 0xff, i+1);\ + add_at(((x) >> 16) & 0xff, i+2);\ + add_at(((x) >> 24) & 0xff, i+3);\ } #define add32(x) {\ add(x & 0xff);\ - add((x >> 8) & 0xff);\ - add((x >> 16) & 0xff);\ - add((x >> 24) & 0xff);\ + add(((x) >> 8) & 0xff);\ + add(((x) >> 16) & 0xff);\ + add(((x) >> 24) & 0xff);\ } #define add64(x) {\ - add32(x & 0xffffffff);\ + add32((x) & 0xffffffff);\ add32((x >> 32) & 0xffffffff);\ } int i = 0; @@ -153,15 +153,15 @@ void* jit(void* arg) { add(0x49); add(0xbd); - add64((uint64_t)t); + add64((uint64_t)&t); while (1) { c = ctx->code[i++]; switch (c.code) { - case INC: add(0x41); add(0x80); add(0x45); add(0x00); add(0x01); break; - case DEC: add(0x41); add(0x80); add(0x6d); add(0x00); add(0x01); break; - case FWD: add(0x49); add(0xff); add(0xc5); break; - case BCK: add(0x49); add(0xff); add(0xcd); break; + case INC: add(0x41); add(0x80); add(0x45); add(0x00); add(c.arg); break; + case DEC: add(0x41); add(0x80); add(0x6d); add(0x00); add(c.arg); break; + case FWD: add(0x41); add(0x80); add(0xC5); add(c.arg); break; + case BCK: add(0x41); add(0x80); add(0xCD); add(c.arg); break; case PRN: add(0x48); add(0xc7); add(0xc0); add(0x01); add(0x00); add(0x00); add(0x00); add(0x48); add(0xc7); add(0xc7); add(0x01); add(0x00); add(0x00); add(0x00); @@ -193,8 +193,8 @@ void* jit(void* arg) { add(0x0F); add(0x85); add32(pcrel_offset); - jmp_to = begin + 6; - jmp_frm = syslen; + jmp_frm = begin + 6; + jmp_to = syslen; pcrel_offset = relative_32bit_offset(jmp_frm, jmp_to); add32_at(pcrel_offset, begin+2); break; From 20fe50b275d1a60a84cde575d48ed68a153ccce2 Mon Sep 17 00:00:00 2001 From: hellerve Date: Mon, 3 Dec 2018 14:37:02 +0100 Subject: [PATCH 04/20] make: fix no_wrap on linux --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3c244eb..b1eddbf 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ all: main.c $(CC) $(MAIN) $(SOURCES) -o $(BUILDDIR)$(TARGET) $(CFLAGS) no_wrap: main.c - CFLAGS+=-DNO_WRAP make + CFLAGS=-DNO_WRAP make dev: main.c mkdir -p $(BUILDDIR) From 3a85187288fb35bbdc59c130196c268da8ec04bd Mon Sep 17 00:00:00 2001 From: hellerve Date: Mon, 3 Dec 2018 14:40:41 +0100 Subject: [PATCH 05/20] eval: remove outdated eval loop --- src/eval.c | 91 +----------------------------------------------------- 1 file changed, 1 insertion(+), 90 deletions(-) diff --git a/src/eval.c b/src/eval.c index ac4bffa..1a0962e 100644 --- a/src/eval.c +++ b/src/eval.c @@ -20,95 +20,6 @@ typedef struct { bytecode* code; } actor_ctx; -/* - We’re using direct threading here. Basically, we avoid looping and case - dispatch by using a computed GOTO instead. This comes at the cost of using a - GNU extension, but I love those anyway. -*/ -void* eval(void* arg) { -#define DISPATCH() { c = ctx->code[i++]; goto *dispatch_table[c.code]; } - int i = 0; - unsigned int h = 0; - unsigned int t[TAPE_LEN]; - actor_ctx* ctx = (actor_ctx*) arg; - bytecode c; - static void* dispatch_table[] = { - &&do_zero, &&do_inc, &&do_dec, &&do_fwd, &&do_bck, &&do_prn, &&do_read, - &&do_startl, &&do_endl, &&do_send, &&do_recv, &&do_move_ptr, &&do_move_data, - &&do_halt - }; - - for (int idx = 0; idx < TAPE_LEN; idx++) t[idx] = 0; - - DISPATCH(); -do_zero: t[h] = 0; DISPATCH(); -do_inc: t[h]+=c.arg; DISPATCH(); -do_dec: t[h]-=c.arg; DISPATCH(); -#ifdef NO_WRAP -do_fwd: h += c.arg; DISPATCH(); -do_bck: h -= c.arg; DISPATCH(); -#else -// modulo would be prettier here, but slows the code down by A LOT; somehow -// the C compilers can’t optimize uncoditional modulos here -do_fwd: h += c.arg; if (h >= TAPE_LEN-1) h %=TAPE_LEN; DISPATCH(); -do_bck: h -= c.arg; if (h < 0) h %= TAPE_LEN; DISPATCH(); -#endif -do_prn: printf("%c", t[h]); DISPATCH(); -do_read: scanf("%c", (char*)&t[h]); DISPATCH(); -do_startl: if(!t[h]) i = c.arg; DISPATCH(); -do_endl: if(t[h]) i = c.arg; DISPATCH(); - -do_send: - if (c.arg) { - if (!ctx->up) { - fprintf(stderr, - "Actor %d tried to write to non-existant up channel, ignoring.", - ctx->num - ); - DISPATCH(); - } - *ctx->up = t[h]; - *ctx->up_written = 1; - DISPATCH(); - } else { - if (!ctx->down) { - fprintf(stderr, - "Actor %d tried to write to non-existant down channel, ignoring.", - ctx->num - ); - DISPATCH(); - } - *ctx->down = t[h]; - *ctx->down_written = 1; - DISPATCH(); - } - -do_recv: - while ((ctx->up_written && !(*ctx->up_written)) && - (ctx->down_written && !(*ctx->down_written))) usleep(100); - if (ctx->up_written && *ctx->up_written) { - t[h] = *ctx->up; - *ctx->up_written = 0; - } else if (ctx->down_written && *ctx->down_written) { - t[h] = *ctx->down; - *ctx->down_written = 0; - } - DISPATCH(); - -do_move_ptr: while (t[h]) h+=c.arg; DISPATCH(); - -do_move_data: - if (t[h]) { - t[h+c.arg] += t[h]; - t[h] = 0; - } - DISPATCH(); - -do_halt: - return NULL; -#undef DISPATCH -} - unsigned int relative_32bit_offset(int jump_frm, int jump_to) { if (jump_to >= jump_frm) return jump_to - jump_frm; else return ~((unsigned int)jump_frm-jump_to) + 1; @@ -141,7 +52,7 @@ void* jit(void* arg) { add32((x >> 32) & 0xffffffff);\ } int i = 0; - unsigned int t[TAPE_LEN]; + unsigned short t[TAPE_LEN]; actor_ctx* ctx = (actor_ctx*) arg; bytecode c; unsigned int syslen = 0; From c207a7ee8e67a172910b1236cb1cd8cdbb914c09 Mon Sep 17 00:00:00 2001 From: hellerve Date: Mon, 3 Dec 2018 14:43:28 +0100 Subject: [PATCH 06/20] jit: add back zero --- src/eval.c | 1 + src/parser.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/eval.c b/src/eval.c index 1a0962e..6f24c82 100644 --- a/src/eval.c +++ b/src/eval.c @@ -71,6 +71,7 @@ void* jit(void* arg) { switch (c.code) { case INC: add(0x41); add(0x80); add(0x45); add(0x00); add(c.arg); break; case DEC: add(0x41); add(0x80); add(0x6d); add(0x00); add(c.arg); break; + case ZERO: add(0x48); add(0x45); add(0x00); add(0x00); add(0x01); break; case FWD: add(0x41); add(0x80); add(0xC5); add(c.arg); break; case BCK: add(0x41); add(0x80); add(0xCD); add(c.arg); break; case PRN: diff --git a/src/parser.c b/src/parser.c index 1e94c21..ca881f8 100644 --- a/src/parser.c +++ b/src/parser.c @@ -89,7 +89,7 @@ actors* parse(char* inpt) { ac->code = NULL; bytecode* res = NULL; inpt = remove_comments(inpt); - //inpt = optimize_zero(inpt); + inpt = optimize_zero(inpt); while (*inpt != '\0') { switch (*inpt) { From c45f7d751814df3e34137a6f3dd522a4556c1278 Mon Sep 17 00:00:00 2001 From: hellerve Date: Mon, 3 Dec 2018 15:17:57 +0100 Subject: [PATCH 07/20] jit: correct zeroing and freeing --- src/eval.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/eval.c b/src/eval.c index 6f24c82..a881fc9 100644 --- a/src/eval.c +++ b/src/eval.c @@ -71,7 +71,7 @@ void* jit(void* arg) { switch (c.code) { case INC: add(0x41); add(0x80); add(0x45); add(0x00); add(c.arg); break; case DEC: add(0x41); add(0x80); add(0x6d); add(0x00); add(c.arg); break; - case ZERO: add(0x48); add(0x45); add(0x00); add(0x00); add(0x01); break; + case ZERO: add(0x4d); add(0x31); add(0xed); break; case FWD: add(0x41); add(0x80); add(0xC5); add(c.arg); break; case BCK: add(0x41); add(0x80); add(0xCD); add(c.arg); break; case PRN: @@ -128,6 +128,7 @@ void* jit(void* arg) { f(); free(sys); + free(mem); return NULL; #undef add From 1ba16c8e90afbc8533fa1bf8e8bb1c85a44d1226 Mon Sep 17 00:00:00 2001 From: hellerve Date: Mon, 3 Dec 2018 15:24:05 +0100 Subject: [PATCH 08/20] eval: munmap, not free --- src/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eval.c b/src/eval.c index a881fc9..28a4355 100644 --- a/src/eval.c +++ b/src/eval.c @@ -127,8 +127,8 @@ void* jit(void* arg) { f(); + munmap(mem, syslen); free(sys); - free(mem); return NULL; #undef add From ed3d850ac0dd38481d074a95e05127f176e255a7 Mon Sep 17 00:00:00 2001 From: hellerve Date: Mon, 3 Dec 2018 16:16:53 +0100 Subject: [PATCH 09/20] eval: fix bck --- src/eval.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/eval.c b/src/eval.c index 28a4355..6e39db5 100644 --- a/src/eval.c +++ b/src/eval.c @@ -72,8 +72,8 @@ void* jit(void* arg) { case INC: add(0x41); add(0x80); add(0x45); add(0x00); add(c.arg); break; case DEC: add(0x41); add(0x80); add(0x6d); add(0x00); add(c.arg); break; case ZERO: add(0x4d); add(0x31); add(0xed); break; - case FWD: add(0x41); add(0x80); add(0xC5); add(c.arg); break; - case BCK: add(0x41); add(0x80); add(0xCD); add(c.arg); break; + case FWD: add(0x41); add(0x80); add(0xc5); add(c.arg); break; + case BCK: add(0x41); add(0x80); add(0xed); add(c.arg); break; case PRN: add(0x48); add(0xc7); add(0xc0); add(0x01); add(0x00); add(0x00); add(0x00); add(0x48); add(0xc7); add(0xc7); add(0x01); add(0x00); add(0x00); add(0x00); @@ -82,17 +82,17 @@ void* jit(void* arg) { add(0x0f); add(0x05); break; case READ: - add(0x48); add(0xC7); add(0xC0); add(0x00); add(0x00); add(0x00); add(0x00); - add(0x48); add(0xC7); add(0xC7); add(0x00); add(0x00); add(0x00); add(0x00); - add(0x4C); add(0x89); add(0xEE); - add(0x48); add(0xC7); add(0xC2); add(0x01); add(0x00); add(0x00); add(0x00); - add(0x0F); add(0x05); + add(0x48); add(0xc7); add(0xc0); add(0x00); add(0x00); add(0x00); add(0x00); + add(0x48); add(0xc7); add(0xc7); add(0x00); add(0x00); add(0x00); add(0x00); + add(0x4c); add(0x89); add(0xee); + add(0x48); add(0xc7); add(0xc2); add(0x01); add(0x00); add(0x00); add(0x00); + add(0x0f); add(0x05); break; case STARTL: add(0x41); add(0x80); add(0x7d); add(0x00); add(0x00); loop_stack[loop_depth++] = syslen; - add(0x0F); add(0x84); add32(0); + add(0x0f); add(0x84); add32(0); break; case ENDL: { int begin = loop_stack[--loop_depth]; @@ -102,7 +102,7 @@ void* jit(void* arg) { int jmp_to = begin + 6; int pcrel_offset = relative_32bit_offset(jmp_frm, jmp_to); - add(0x0F); add(0x85); + add(0x0f); add(0x85); add32(pcrel_offset); jmp_frm = begin + 6; From f5e8544b9251fbf3613983f9f6b968624ab5d612 Mon Sep 17 00:00:00 2001 From: hellerve Date: Mon, 3 Dec 2018 16:32:08 +0100 Subject: [PATCH 10/20] eval: jit works on apple --- src/eval.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/eval.c b/src/eval.c index 6e39db5..f3bba47 100644 --- a/src/eval.c +++ b/src/eval.c @@ -75,14 +75,22 @@ void* jit(void* arg) { case FWD: add(0x41); add(0x80); add(0xc5); add(c.arg); break; case BCK: add(0x41); add(0x80); add(0xed); add(c.arg); break; case PRN: +#ifdef __APPLE__ + add(0x48); add(0xc7); add(0xc0); add(0x04); add(0x00); add(0x00); add(0x02); +#else add(0x48); add(0xc7); add(0xc0); add(0x01); add(0x00); add(0x00); add(0x00); +#endif add(0x48); add(0xc7); add(0xc7); add(0x01); add(0x00); add(0x00); add(0x00); add(0x4c); add(0x89); add(0xee); add(0x48); add(0xc7); add(0xc2); add(0x01); add(0x00); add(0x00); add(0x00); add(0x0f); add(0x05); break; case READ: +#ifdef __APPLE__ + add(0x48); add(0xc7); add(0xc0); add(0x03); add(0x00); add(0x00); add(0x02); +#else add(0x48); add(0xc7); add(0xc0); add(0x00); add(0x00); add(0x00); add(0x00); +#endif add(0x48); add(0xc7); add(0xc7); add(0x00); add(0x00); add(0x00); add(0x00); add(0x4c); add(0x89); add(0xee); add(0x48); add(0xc7); add(0xc2); add(0x01); add(0x00); add(0x00); add(0x00); From 5e2c26211183ddb5ea6b8ffcf81fa4b90ec44f2b Mon Sep 17 00:00:00 2001 From: hellerve Date: Mon, 3 Dec 2018 17:52:43 +0100 Subject: [PATCH 11/20] jit: use mov for zero --- Makefile | 2 +- src/eval.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index b1eddbf..b600f30 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ PREFIX=/usr/local/bin/ SOURCES=$(wildcard src/*.c) MAIN=main.c override CFLAGS+=-std=c11 -O2 -Wno-gnu -lpthread -DEVFLAGS=-Werror -Wall -g -fPIC -DNDEBUG -Wfloat-equal -Wundef -Wwrite-strings -Wuninitialized -pedantic -fsanitize=address -O0 +DEVFLAGS=-Werror -Wall -g -fPIC -DNDEBUG -Wfloat-equal -Wundef -Wwrite-strings -Wuninitialized -pedantic -O0 all: main.c mkdir -p $(BUILDDIR) diff --git a/src/eval.c b/src/eval.c index f3bba47..653ab24 100644 --- a/src/eval.c +++ b/src/eval.c @@ -71,7 +71,7 @@ void* jit(void* arg) { switch (c.code) { case INC: add(0x41); add(0x80); add(0x45); add(0x00); add(c.arg); break; case DEC: add(0x41); add(0x80); add(0x6d); add(0x00); add(c.arg); break; - case ZERO: add(0x4d); add(0x31); add(0xed); break; + case ZERO: add(0x41); add(0xc6); add(0x45); add(0x00); add(0x00); break; case FWD: add(0x41); add(0x80); add(0xc5); add(c.arg); break; case BCK: add(0x41); add(0x80); add(0xed); add(c.arg); break; case PRN: From 718f7753e70c84ebba2a1a448dea1d4ecd8b84cb Mon Sep 17 00:00:00 2001 From: hellerve Date: Mon, 3 Dec 2018 18:13:50 +0100 Subject: [PATCH 12/20] jit: ignore untreated opcodes for now --- src/eval.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/eval.c b/src/eval.c index 653ab24..4e2a5a7 100644 --- a/src/eval.c +++ b/src/eval.c @@ -120,8 +120,9 @@ void* jit(void* arg) { break; } case HALT: - default: goto jit_start; + default: + break; } } jit_start: From 74f3ebe6365519679661ecc1fbed4fd1f4e84804 Mon Sep 17 00:00:00 2001 From: hellerve Date: Mon, 3 Dec 2018 20:59:34 +0100 Subject: [PATCH 13/20] debug: add faux asm; jit: prettier relative offsets --- src/debug.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/debug.h | 1 + src/eval.c | 32 +++++++++++++++-------- 3 files changed, 95 insertions(+), 11 deletions(-) diff --git a/src/debug.c b/src/debug.c index 70011d1..0a2fde7 100644 --- a/src/debug.c +++ b/src/debug.c @@ -86,3 +86,76 @@ void print_src(actors* ac) { printf("\n\n"); } } + +uint32_t calc_offset(uint32_t jump_frm, uint32_t jump_to) { + if (jump_to >= jump_frm) return jump_to - jump_frm; + else return ~((uint32_t)jump_frm-jump_to) + 1; +} + +void print_faux_assembly_bytecode(long offset, bytecode* code) { +#define print(s) (printf("0x%lx: %s\n", idx+offset, s)) +#define print_arg(s) (printf("0x%lx: %s $0x%x, (%%r13)\n", idx+offset, s, c.arg)) +#define print_arg_ref(s) (printf("0x%lx: %s $0x%x, %%r13b\n", idx+offset, s, c.arg)) +#define print_syscall(d) {\ + printf("0x%lx: movq 0x200000%d, %%rax\n", idx+offset, d);\ + idx+=7;\ + print("movq 0x1, %%rdi");\ + idx+=7;\ + print("movq %%r13, %%rsi");\ + idx+=3;\ + print("movq 0x1, %%rdx");\ + idx+=7;\ + print("syscall ");\ + idx+=2;\ +} + int i = 0; + long idx = 10; + size_t loop_stack[256]; + int loop_depth = 0; + printf("0x%lx: movabsq $random-addr, %%r13\n", offset); + while(1) { + bytecode c = code[i++]; + switch (c.code) { + case INC: print_arg("addb"); idx+=5;break; + case DEC: print_arg("subb"); idx+=5;break; + case FWD: print_arg_ref("addb"); idx+=4;break; + case BCK: print_arg_ref("subb"); idx+=4;break; + case PRN: print_syscall(4); break; + case ZERO: print("movb $0x0, (%r13)"); idx+=5;break; + case READ: print_syscall(3); break; + case STARTL: + print("cmpb $0x0, (%r13)"); + idx+=5; + printf("0x%lx: je 0x%lx\n", idx+offset, c.arg+offset); + loop_stack[loop_depth++] = idx; + idx+=6; + break; + case ENDL: { + size_t begin = loop_stack[--loop_depth]; + + print("cmpb $0x0, (%r13)"); + idx+=5; + + size_t jmp_frm = idx + 6; + size_t jmp_to = begin + 6; + uint32_t pcrel_offset = calc_offset(jmp_frm, jmp_to); + printf("0x%lx: jne 0x%x\n", idx+offset, pcrel_offset); + idx+=6; + break; + } + case HALT: + print("retq"); + return; + } + } +#undef print +#undef print_arg +#undef print_arg_ref +#undef print_syscall +} + +void print_faux_assembly(long offset, actors* ac) { + for (int i = 0; i < ac->num; i++) { + print_faux_assembly_bytecode(offset, ac->code[i]); + } +} diff --git a/src/debug.h b/src/debug.h index cd8963d..44e6a77 100644 --- a/src/debug.h +++ b/src/debug.h @@ -5,5 +5,6 @@ void print_actors(actors*); void print_src(actors*); +void print_faux_assembly(long, actors*); #endif diff --git a/src/eval.c b/src/eval.c index 4e2a5a7..45a8150 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1,8 +1,10 @@ #define _DEFAULT_SOURCE +#include #include #include #include #include +#include #include #include #include @@ -20,9 +22,17 @@ typedef struct { bytecode* code; } actor_ctx; -unsigned int relative_32bit_offset(int jump_frm, int jump_to) { - if (jump_to >= jump_frm) return jump_to - jump_frm; - else return ~((unsigned int)jump_frm-jump_to) + 1; +uint32_t relative_32bit_offset(uint32_t jump_frm, uint32_t jump_to) { + if (jump_to >= jump_frm) { + size_t diff = jump_to - jump_frm; + assert(diff < (1ull << 31)); + return diff; + } else { + size_t diff = jump_frm - jump_to; + assert(diff-1 < (1ull << 31)); + uint32_t diff_unsigned = (uint32_t)diff; + return ~diff_unsigned + 1; + } } typedef void (*jit_fn)(void); @@ -52,12 +62,12 @@ void* jit(void* arg) { add32((x >> 32) & 0xffffffff);\ } int i = 0; - unsigned short t[TAPE_LEN]; + uint8_t t[TAPE_LEN]; actor_ctx* ctx = (actor_ctx*) arg; bytecode c; - unsigned int syslen = 0; - unsigned char* sys = NULL; - int loop_stack[256]; + size_t syslen = 0; + uint8_t* sys = NULL; + size_t loop_stack[2048]; int loop_depth = 0; for (int idx = 0; idx < TAPE_LEN; idx++) t[idx] = 0; @@ -103,12 +113,12 @@ void* jit(void* arg) { add(0x0f); add(0x84); add32(0); break; case ENDL: { - int begin = loop_stack[--loop_depth]; + size_t begin = loop_stack[--loop_depth]; add(0x41); add(0x80); add(0x7d); add(0x00); add(0x00); - int jmp_frm = syslen + 6; - int jmp_to = begin + 6; - int pcrel_offset = relative_32bit_offset(jmp_frm, jmp_to); + size_t jmp_frm = syslen + 6; + size_t jmp_to = begin + 6; + uint32_t pcrel_offset = relative_32bit_offset(jmp_frm, jmp_to); add(0x0f); add(0x85); add32(pcrel_offset); From 2d747da7961759fa883566793829335faf146883 Mon Sep 17 00:00:00 2001 From: hellerve Date: Mon, 3 Dec 2018 21:05:32 +0100 Subject: [PATCH 14/20] debug: forgot include --- src/debug.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/debug.c b/src/debug.c index 0a2fde7..9203806 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1,3 +1,4 @@ +#include #include #include From bcdfc93904fcee9e8a97183d380fd3f5157b0c85 Mon Sep 17 00:00:00 2001 From: hellerve Date: Mon, 3 Dec 2018 21:19:04 +0100 Subject: [PATCH 15/20] all: gcc fixes --- main.c | 4 +++- src/parser.c | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/main.c b/main.c index 16a8cd0..f40d552 100644 --- a/main.c +++ b/main.c @@ -26,9 +26,11 @@ int main(int argc, char** argv) { fseek(f, 0, SEEK_SET); char *string = malloc(fsize + 1); - fread(string, fsize, 1, f); + int read = fread(string, 1, fsize, f); fclose(f); + if (read != fsize) return err("couldn’t read file %s.\n", argv[1]); + string[fsize] = 0; actors* ac = parse(string); diff --git a/src/parser.c b/src/parser.c index ca881f8..fb07800 100644 --- a/src/parser.c +++ b/src/parser.c @@ -57,7 +57,8 @@ int optimize_loop(bytecode* s, int begin, int end) { s[begin-1] = (bytecode){.code=MOVE_PTR, .arg=narg}; return 2; } else if (end-begin == 5 && rep.code == DEC && s[begin+2].code == INC && - rep.arg == s[begin+2].arg == 1 && s[begin+1].arg == s[begin+3].arg) { + rep.arg == s[begin+2].arg && rep.arg == 1 && + s[begin+1].arg == s[begin+3].arg) { // [->+<] || [-<+>] == MOVE_DATA if (s[begin+1].code == FWD && s[begin+3].code == BCK) { s[begin-1] = (bytecode){.code=MOVE_DATA, .arg=s[begin+1].arg}; From 123fd876fb8a2efb11b57ee0c55e474731e14ed4 Mon Sep 17 00:00:00 2001 From: hellerve Date: Mon, 3 Dec 2018 21:19:50 +0100 Subject: [PATCH 16/20] parser: remove dead var --- src/parser.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/parser.c b/src/parser.c index fb07800..36c1a20 100644 --- a/src/parser.c +++ b/src/parser.c @@ -82,7 +82,6 @@ actors* parse(char* inpt) { } int idx = 0; int dup = 0; - char* str = inpt; int loop_stack[256]; int loop_depth = 0; actors* ac = malloc(sizeof(actors)); @@ -141,7 +140,6 @@ actors* parse(char* inpt) { res = NULL; } idx = 0; - str = inpt+1; } else if (*inpt == '\0') continue; break; } From 176fcde789215ee53a81ff1f3e43eea8408c8fca Mon Sep 17 00:00:00 2001 From: hellerve Date: Tue, 4 Dec 2018 08:38:46 +0100 Subject: [PATCH 17/20] jit: works --- src/eval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eval.c b/src/eval.c index 45a8150..f3e4751 100644 --- a/src/eval.c +++ b/src/eval.c @@ -82,8 +82,8 @@ void* jit(void* arg) { case INC: add(0x41); add(0x80); add(0x45); add(0x00); add(c.arg); break; case DEC: add(0x41); add(0x80); add(0x6d); add(0x00); add(c.arg); break; case ZERO: add(0x41); add(0xc6); add(0x45); add(0x00); add(0x00); break; - case FWD: add(0x41); add(0x80); add(0xc5); add(c.arg); break; - case BCK: add(0x41); add(0x80); add(0xed); add(c.arg); break; + case FWD: add(0x49); add(0x83); add(0xc5); add(c.arg); break; + case BCK: add(0x49); add(0x83); add(0xed); add(c.arg); break; case PRN: #ifdef __APPLE__ add(0x48); add(0xc7); add(0xc0); add(0x04); add(0x00); add(0x00); add(0x02); From 01a5656eba4031e1026bd301afe7e29cf3bbd01e Mon Sep 17 00:00:00 2001 From: hellerve Date: Tue, 4 Dec 2018 15:09:53 +0100 Subject: [PATCH 18/20] jit: works completely; better example --- Makefile | 2 +- example/hello_world.bf | 5 -- example/ping.bf | 5 ++ src/eval.c | 114 +++++++++++++++++++++++++++++++++-------- src/parser.c | 32 ------------ 5 files changed, 99 insertions(+), 59 deletions(-) delete mode 100644 example/hello_world.bf create mode 100644 example/ping.bf diff --git a/Makefile b/Makefile index b600f30..bcdc113 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ BUILDDIR=bin/ PREFIX=/usr/local/bin/ SOURCES=$(wildcard src/*.c) MAIN=main.c -override CFLAGS+=-std=c11 -O2 -Wno-gnu -lpthread +override CFLAGS+=-std=c11 -O2 -g -Wno-gnu -lpthread DEVFLAGS=-Werror -Wall -g -fPIC -DNDEBUG -Wfloat-equal -Wundef -Wwrite-strings -Wuninitialized -pedantic -O0 all: main.c diff --git a/example/hello_world.bf b/example/hello_world.bf deleted file mode 100644 index 3a503a4..0000000 --- a/example/hello_world.bf +++ /dev/null @@ -1,5 +0,0 @@ -++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.<+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.>u.<. - -++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>. diff --git a/example/ping.bf b/example/ping.bf new file mode 100644 index 0000000..0667a0e --- /dev/null +++ b/example/ping.bf @@ -0,0 +1,5 @@ ++++++++++++++++++++++++++++++++++++[>++>++>++>++<<<<-]>++++++++++>+++>++++++++>+>++++++++++<<<<< +++++++++++[>.>.>.>.>.>vu<<<<<<-] + ++++++++++++++++++++++++++++++++++++[>++>++>++>++<<<<-]>++++++++++>+++++++++>++++++++>+>++++++++++<<<<< +++++++++++[>>>>>>u<<<<<<>.>.>.>.>.^<<<<<-] diff --git a/src/eval.c b/src/eval.c index f3e4751..7a8c390 100644 --- a/src/eval.c +++ b/src/eval.c @@ -15,13 +15,53 @@ typedef struct { int num; - int* up; - int* up_written; - int* down; - int* down_written; + int* up_in; + int* up_written_in; + int* up_out; + int* up_written_out; + int* down_in; + int* down_written_in; + int* down_out; + int* down_written_out; bytecode* code; } actor_ctx; +void recv(actor_ctx* ctx, int* cl) { + while ((!ctx->up_written_in || !(*ctx->up_written_in)) && + (!ctx->down_written_in || !(*ctx->down_written_in))) usleep(100); + if (ctx->up_written_in && *ctx->up_written_in) { + *cl = *ctx->up_in; + *ctx->up_written_in = 0; + } else if (ctx->down_written_in && *ctx->down_written_in) { + *cl = *ctx->down_in; + *ctx->down_written_in = 0; + } +} + +void send_down(actor_ctx* ctx, int* cl) { + if (!ctx->down_out) { + fprintf(stderr, + "Actor %d tried to write to non-existant down channel, ignoring.", + ctx->num + ); + return; + } + *ctx->down_out = *cl; + *ctx->down_written_out = 1; +} + +void send_up(actor_ctx* ctx, int* cl) { + if (!ctx->up_out) { + fprintf(stderr, + "Actor %d tried to write to non-existant up channel, ignoring.", + ctx->num + ); + return; + } + *ctx->up_out = *cl; + *ctx->up_written_out = 1; +} + uint32_t relative_32bit_offset(uint32_t jump_frm, uint32_t jump_to) { if (jump_to >= jump_frm) { size_t diff = jump_to - jump_frm; @@ -52,14 +92,14 @@ void* jit(void* arg) { add_at(((x) >> 24) & 0xff, i+3);\ } #define add32(x) {\ - add(x & 0xff);\ + add((x) & 0xff);\ add(((x) >> 8) & 0xff);\ add(((x) >> 16) & 0xff);\ add(((x) >> 24) & 0xff);\ } #define add64(x) {\ add32((x) & 0xffffffff);\ - add32((x >> 32) & 0xffffffff);\ + add32(((x) >> 32) & 0xffffffff);\ } int i = 0; uint8_t t[TAPE_LEN]; @@ -129,6 +169,23 @@ void* jit(void* arg) { add32_at(pcrel_offset, begin+2); break; } + case RECV: { + add(0x55); + add(0x4c); add(0x89); add(0xee); + add(0x48); add(0xbf); add64((uint64_t)ctx); + add(0x49); add(0xbe); add64((uint64_t)recv); + add(0x41); add(0xff); add(0xd6); + add(0x5d); + break; + } + case SEND: { + add(0x55); + add(0x48); add(0xbf); add64((uint64_t)ctx); + add(0x49); add(0xbe); add64((uint64_t)(c.arg ? send_up : send_down)); + add(0x41); add(0xff); add(0xd6); + add(0x5d); + break; + } case HALT: goto jit_start; default: @@ -147,7 +204,7 @@ void* jit(void* arg) { f(); munmap(mem, syslen); - free(sys); + //free(sys); return NULL; #undef add @@ -160,39 +217,54 @@ void eval_actors(actors* ac) { int i; pthread_t ts[ac->num]; actor_ctx ctxs[ac->num]; - int* down = NULL; - int* down_written = NULL; + int* down_in = NULL; + int* down_written_in = NULL; + int* down_out = NULL; + int* down_written_out = NULL; for (i = 0; i < ac->num; i++) { actor_ctx ctx; ctx.num = i; - ctx.down = down; - ctx.down_written = down_written; + ctx.down_in = down_in; + ctx.down_written_in = down_written_in; + ctx.down_out = down_out; + ctx.down_written_out = down_written_out; if (i < ac->num-1) { - ctx.up = malloc(sizeof(int)); - ctx.up_written = malloc(sizeof(int)); - *ctx.up_written = 0; + ctx.up_in = malloc(sizeof(int)); + ctx.up_written_in = malloc(sizeof(int)); + *ctx.up_written_in = 0; + ctx.up_out = malloc(sizeof(int)); + ctx.up_written_out = malloc(sizeof(int)); + *ctx.up_written_out = 0; } else { - ctx.up = NULL; - ctx.up_written = NULL; + ctx.up_in = NULL; + ctx.up_written_in = NULL; + ctx.up_out = NULL; + ctx.up_written_out = NULL; } ctx.code = ac->code[i]; ctxs[i] = ctx; if(ac->num == 1) { // if we just have one thread, we eval it directly; buys us about 2% perf jit(&ctx); - free(ctx.up); - free(ctx.up_written); + free(ctx.up_in); + free(ctx.up_written_in); + free(ctx.up_out); + free(ctx.up_written_out); return; } else { pthread_create(&ts[i], NULL, jit, &ctxs[i]); } - down = ctx.up; - down_written = ctx.up_written; + down_in = ctx.up_out; + down_written_in = ctx.up_written_out; + down_out = ctx.up_in; + down_written_out = ctx.up_written_in; } for (i = 0; i < ac->num; i++) { pthread_join(ts[i], NULL); + } + for (i = 0; i < ac->num; i++) { actor_ctx ctx = ctxs[i]; - if (ctx.up) { free(ctx.up); free(ctx.up_written); } + if (ctx.up_in) { free(ctx.up_in); free(ctx.up_written_in); free(ctx.up_out); free(ctx.up_written_out); } } } diff --git a/src/parser.c b/src/parser.c index 36c1a20..ff0aaac 100644 --- a/src/parser.c +++ b/src/parser.c @@ -49,28 +49,6 @@ char* optimize_zero(char* inpt) { return inpt; } -int optimize_loop(bytecode* s, int begin, int end) { - bytecode rep = s[begin]; - if (end-begin == 2 && (rep.code == FWD || rep.code == BCK)) { - // [>] || [<] == MOVE_PTR - int narg = rep.code == FWD ? rep.arg : -rep.arg; - s[begin-1] = (bytecode){.code=MOVE_PTR, .arg=narg}; - return 2; - } else if (end-begin == 5 && rep.code == DEC && s[begin+2].code == INC && - rep.arg == s[begin+2].arg && rep.arg == 1 && - s[begin+1].arg == s[begin+3].arg) { - // [->+<] || [-<+>] == MOVE_DATA - if (s[begin+1].code == FWD && s[begin+3].code == BCK) { - s[begin-1] = (bytecode){.code=MOVE_DATA, .arg=s[begin+1].arg}; - return 5; - } else if (s[begin+1].code == BCK && s[begin+3].code == FWD) { - s[begin-1] = (bytecode){.code=MOVE_DATA, .arg=-s[begin+1].arg}; - return 5; - } - } - return 0; -} - actors* parse(char* inpt) { #define build_op(c, a) {\ res = realloc(res, sizeof(bytecode)*(++idx));\ @@ -114,16 +92,6 @@ actors* parse(char* inpt) { } build_op(ENDL, matching_begin+1); - //int removed = optimize_loop(res, matching_begin+1, idx); - //idx -= removed; - //dup += removed; - //res = realloc(res, sizeof(bytecode)*idx); - //if (!removed) { - // res[matching_begin].arg = idx; - // if (res[matching_begin].code != STARTL && idx < 30) { - // printf("wat: %d %d\n", matching_begin, idx); - // } - //} break; } From 8e944dc2a980e7586d9d96b4be2db64d59e82dff Mon Sep 17 00:00:00 2001 From: hellerve Date: Tue, 4 Dec 2018 15:13:38 +0100 Subject: [PATCH 19/20] readme: rewrite --- README.md | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index ac283a0..b5ac8fd 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ actor that is effectively a Brainfuck program with added primitives for receiving from and sending to other actors. +This is the JIT version. + ## Usage You can build the program through its Makefile by typing `make`. This will leave @@ -43,35 +45,21 @@ And that’s all that `cspfuck` is. I’m a terrible Brainfuck programmer, so I only provide one proof-of-concept example for now. It is contained in a directory named -`example/`—appropriately singular—, in which I provide a hello world program. -It is three actors, printing hello world concurrently. At the end of the -program, the first actor goes back to the cell containing `d`, and sends it -to the actor below, which will receive it into a new cell, print it, and print -a newline to make it pretty. +`example/`—appropriately singular—, in which I provide a ping pong program. +It contains two actors, one printing ping, then waiting for the other, the +other waiting, and then printing pong, ten times. ## Implementation -Actors are implemented as pthreads. The virtual machine is a simple direct -threaded bytecode VM that offers 30,000 elements to each Brainfuck program. It -will wrap around if you go past the low or high threshold—this can be turned -off by passing `-DNO_WRAP` to the compiler or calling `make no_wrap`, as -disabling it might buy you a little bit of performance, depending on your -workfload. - -It should be reasonably performant, but who cares? I hope noone’s going to run -their MapReduce jobs on it. There are some low-hanging fruits for optimization, -like making the VM loop use direct threaded code, but I chose not to for now. -Feel free to hack on it you want to! I’m happy to help you get started. +Actors are implemented as pthreads. The virtual machine is a simple handrolled +JIT that offers 30,000 elements to each Brainfuck program. It will segfault if +you go past the low or high threshold. This version is about four times faster +than the bytecode VM provided in the [`master`](https://github.com/hellerve/cspfuck/tree/master) branch. -The VM does seem to execute [ridiculous programs](http://www.clifford.at/bfcpu/hanoi.html) -in standard Brainfuck pretty efficiently, which makes me unreasonably happy. - -It’s only about 400 lines of C, so it should be reasonably consumable. The +It’s only about 550 lines of C, so it should be reasonably consumable. The code isn’t necessarily pretty, but it seems to work well. It is not incredibly battle-tested, though. -If you want to know more, read [my blog post](http://blog.veitheller.de/Brainfuck_and_Actors.html)! - **Disclaimer**: I know approximately as much about concurrent programming in C as I know about writing production-grade Brainfuck. The system should be expected to be brittle. From 85c72e956ddab3b5325454a5eb59e64eb72f5253 Mon Sep 17 00:00:00 2001 From: hellerve Date: Wed, 2 Jan 2019 14:02:24 +0100 Subject: [PATCH 20/20] readme: add link to speeding up an interpreter --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b5ac8fd..01aae1b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ actor that is effectively a Brainfuck program with added primitives for receiving from and sending to other actors. -This is the JIT version. +This is the JIT version. Read [my write-up](https://blog.veitheller.de/Speeding_up_an_Interpreter.html) +about it. ## Usage