aboutsummaryrefslogtreecommitdiff
path: root/sjit_c.c
diff options
context:
space:
mode:
Diffstat (limited to 'sjit_c.c')
-rw-r--r--sjit_c.c218
1 files changed, 218 insertions, 0 deletions
diff --git a/sjit_c.c b/sjit_c.c
new file mode 100644
index 0000000..85702fc
--- /dev/null
+++ b/sjit_c.c
@@ -0,0 +1,218 @@
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+
+enum instr {
+ PushRef,
+ PushI,
+ Put,
+ Pop,
+
+ Call,
+ Ret,
+ Halt,
+
+ IAddRet,
+ IMulRet,
+ ISubRet,
+ IDivRet
+};
+
+static uint32_t instr_size(enum instr instr) {
+ switch (instr) {
+ case PushRef: return 5+1;
+ case PushI: return 7+1;
+ case Put: return 1+5;
+ case Pop: return 4;
+
+ case Call: return 5;
+ case Ret: return 1;
+ case Halt: return 1+1;
+
+ case IAddRet:
+ case ISubRet: return 5+5+3+5+1;
+ case IMulRet: return 5+5+4+5+1;
+ case IDivRet: return 5+5+3+3+5+1;
+ }
+}
+
+static inline void gen_instr(char *full_code, char **code_p, uint64_t **pgm_p, uint32_t *mapping) {
+ uint64_t arg;
+
+ char *code=*code_p;
+ uint64_t *pgm=*pgm_p;
+
+ switch (*pgm) {
+ case PushRef:
+ arg=pgm[1];
+#ifdef DEBUG_JIT_INSTRUCTIONS
+ fprintf(stderr,"PushRef %lu\n",arg);
+#endif
+ code[0]='\x48'; /* mov rbx,[rsp+ARG*8] */
+ code[1]='\x8b';
+ code[2]='\x5c';
+ code[3]='\x24';
+ code[4]=(unsigned char)arg*8;
+ code[5]='\x53'; /* push rbx */
+ pgm+=2;
+ code+=6;
+ break;
+ case PushI:
+ arg=pgm[1];
+#ifdef DEBUG_JIT_INSTRUCTIONS
+ fprintf(stderr,"PushI %lu\n",arg);
+#endif
+ code[0]='\x48'; /* mov rbx,ARG */
+ code[1]='\xc7';
+ code[2]='\xc3';
+ *(uint32_t*)&code[3]=(uint32_t)arg;
+ code[7]='\x53'; /* push rbx */
+ pgm+=2;
+ code+=8;
+ break;
+ case Put:
+ arg=pgm[1];
+#ifdef DEBUG_JIT_INSTRUCTIONS
+ fprintf(stderr,"Put %lu\n",arg);
+#endif
+ code[0]='\x5b'; /* pop rbx */
+ code[1]='\x48'; /* mov [rsp+ARG*8],rbx */
+ code[2]='\x89';
+ code[3]='\x5c';
+ code[4]='\x24';
+ code[5]=(unsigned char)(arg-1)*8;
+ pgm+=2;
+ code+=6;
+ break;
+ case Pop:
+ arg=pgm[1];
+#ifdef DEBUG_JIT_INSTRUCTIONS
+ fprintf(stderr,"Pop %lu\n",arg);
+#endif
+ code[0]='\x48'; /* add rsp,ARG*8 */
+ code[1]='\x83';
+ code[2]='\xc4';
+ code[3]=(unsigned char)arg*8;
+ pgm+=2;
+ code+=4;
+ break;
+
+ case Call:
+ arg=pgm[1];
+#ifdef DEBUG_JIT_INSTRUCTIONS
+ fprintf(stderr,"Call %lu -> %d\n",arg,mapping[arg]-(&code[5]-full_code));
+#endif
+ code[0]='\xe8'; /* callq ARG */
+ *(uint32_t*)&code[1]=mapping[arg]-(&code[5]-full_code);
+ pgm+=2;
+ code+=5;
+ break;
+ case Ret:
+#ifdef DEBUG_JIT_INSTRUCTIONS
+ fprintf(stderr,"Ret\n");
+#endif
+ code[0]='\xc3'; /* retq */
+ pgm++;
+ code+=1;
+ break;
+ case Halt:
+#ifdef DEBUG_JIT_INSTRUCTIONS
+ fprintf(stderr,"Halt\n");
+#endif
+ code[0]='\x58'; /* pop rax */
+ code[1]='\xc3'; /* retq */
+ pgm++;
+ code+=2;
+ break;
+
+ case IAddRet:
+ case IMulRet:
+ case ISubRet:
+ case IDivRet:
+#ifdef DEBUG_JIT_INSTRUCTIONS
+ fprintf(stderr,"I<Op>Ret\n");
+#endif
+ /* mov rax,[rsp+8] */
+ code[0]='\x48'; code[1]='\x8b'; code[2]='\x44'; code[3]='\x24'; code[4]='\x08';
+ /* mov rbx,[rsp+16] */
+ code[5]='\x48'; code[6]='\x8b'; code[7]='\x5c'; code[8]='\x24'; code[9]='\x10';
+ switch (*pgm) {
+ case IAddRet:
+ case ISubRet:
+ /* {add,sub} rax,rbx */
+ code[10]='\x48';
+ code[11]=*pgm==IAddRet ? '\x01' : '\x29';
+ code[12]='\xd8';
+ code+=13;
+ break;
+ case IMulRet:
+ /* imul rax,rbx */
+ code[10]='\x48'; code[11]='\x0f'; code[12]='\xaf'; code[13]='\xc3';
+ code+=14;
+ break;
+ case IDivRet:
+ /* xor rdx,rdx */
+ code[10]='\x48'; code[11]='\x31'; code[12]='\xd2';
+ /* idiv rbx */
+ code[13]='\x48'; code[14]='\xf7'; code[15]='\xfb';
+ code+=16;
+ break;
+ }
+ /* mov [rsp+16],rax */
+ code[0]='\x48'; code[1]='\x89'; code[2]='\x44'; code[3]='\x24'; code[4]='\x10';
+ /* retq */
+ code[5]='\xc3';
+ pgm++;
+ code+=6;
+ break;
+
+ default:
+ fprintf(stderr,"unknown instruction %lu\n",pgm[-1]);
+ exit(1);
+ }
+
+ *code_p=code;
+ *pgm_p=pgm;
+}
+
+unsigned int jit(uint64_t *pgm) {
+ uint32_t len=*pgm++;
+ uint32_t i;
+
+ uint32_t *mapping=malloc(len*sizeof(uint32_t));
+ uint32_t code_i=0;
+
+ uint64_t *pgm_p=pgm;
+ for (i=0; i<len; i++) {
+ enum instr instr=(enum instr)*pgm_p;
+
+ mapping[i]=code_i;
+ code_i+=instr_size(instr);
+
+ switch (instr) {
+ case PushRef:
+ case PushI:
+ case Put:
+ case Pop:
+ case Call:
+ pgm_p+=2;
+ break;
+ default:
+ pgm_p+=1;
+ break;
+ }
+ }
+
+ char *code=mmap(NULL, code_i, PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
+ char *code_wr=code;
+ pgm_p=pgm;
+ for (i=0; i<len; i++)
+ gen_instr(code, &code_wr, &pgm_p, mapping);
+
+ mprotect(code, code_i, PROT_READ | PROT_EXEC);
+ int (*fun)(void) = (int(*)(void)) code;
+
+ return fun();
+}