//usr/bin/cc "$0" -o "$0.bin" && exec "./$0.bin" "$@" /* Simple little RISCy bytecode interpreter as a sort of quick spike * to see how fast or slow it goes. Answer: about 20× slower than * GCC on the same machine. */ #include #include #include #include typedef uint32_t u32; typedef struct { u32 reg[16]; u32 PC, SP; u32 *mem; } machine; enum opcode { insn_push = 0x21, insn_pop, insn_lit16, insn_low16, insn_add, insn_sub, insn_jl, insn_halt, insn_call, insn_ret, insn_mov }; static inline int src_reg(u32 insn) { return (insn >> 20) & 0xf; } static inline int dst_reg(u32 insn) { return (insn >> 16) & 0xf; } #define IF break; case #define ELSE break; default int interpret(machine *m, int a, int b, int c, int d) { register u32 *mem = m->mem, *pcp = m->PC + mem, *spp = m->SP + mem; m->reg[0] = a; m->reg[1] = b; m->reg[2] = c; m->reg[3] = d; for (;;) { u32 insn = *pcp++, *dest; enum opcode op = (insn & 0xff000000u) >> 24; switch(op) { IF insn_push: spp--; *spp = m->reg[src_reg(insn)]; IF insn_pop: m->reg[dst_reg(insn)] = *spp; spp++; IF insn_lit16: m->reg[dst_reg(insn)] = (insn & 0xffff) | (insn & 0x8000 ? 0xffff0000u : 0); IF insn_low16: m->reg[dst_reg(insn)] <<= 16; m->reg[dst_reg(insn)] |= insn & 0xffff; abort(); // untested code IF insn_add: m->reg[dst_reg(insn)] += m->reg[src_reg(insn)]; IF insn_sub: m->reg[dst_reg(insn)] -= m->reg[src_reg(insn)]; IF insn_jl: if (m->reg[src_reg(insn)] & (1 << 31)) { pcp += (insn & 0xffff) | (insn & 0x8000 ? 0xffff0000u : 0); } IF insn_halt: m->PC = pcp - mem; m->SP = spp - mem; return m->reg[0]; IF insn_call: dest = pcp + ((insn & 0xffff) | (insn & 0x8000 ? 0xffff0000 : 0)); spp--; *spp = pcp - mem; pcp = dest; IF insn_ret: pcp = *spp + mem; spp++; IF insn_mov: m->reg[dst_reg(insn)] = m->reg[src_reg(insn)]; ELSE: abort(); /* invalid instruction */ } } } /* assemble register-register instruction */ #define a_rr(n, s, d) ((insn_##n << 24) | ((s) << 20) | ((d) << 16)) /* assemble register-dest instruction */ #define a_rd(n, d) a_rr(n, 0, (d)) /* assemble register-source instruction */ #define a_rs(n, s) a_rr(n, (s), 0) #define a_k16(n, r, k) ((insn_##n << 24) | ((r) << 16) | ((k) & 0xFfFf)) #define a_jl(s, off) ((insn_jl << 24) | ((s) << 20) | ((off) & 0xFfFf)) #define a_call(off) ((insn_call << 24) | ((off) & 0xFfFf)) #define a_ret (insn_ret << 24) #define a_halt (insn_halt << 24) /* dumb fibonacci: if r0 < 2 then 1 else fib(r0 - 1) + fib(r0 - 2) */ u32 program[] = { a_call(1), /* call fib */ a_halt, a_rr(mov, 0, 1), /* fib: r1 := r0 */ a_k16(lit16, 2, 2), /* r2 := 2 */ a_rr(sub, 2, 1), /* r1 -= r2 */ a_jl(1, 13), /* if r1 < 0, go forward 13 insns */ a_rs(push, 0), /* push r0 */ a_k16(lit16, 3, 1), /* r3 := 1 */ a_rr(sub, 3, 0), /* r0 -= r3 */ a_call(-8), /* call fib */ a_rd(pop, 1), /* pop input r0 into r1 */ a_rs(push, 0), /* save return value from recursive call */ a_k16(lit16, 3, 2), /* r3 := 2 */ a_rr(mov, 1, 0), /* r0 := r1 */ a_rr(sub, 3, 0), /* r0 -= r3 */ a_call(-14), /* call fib */ a_rd(pop, 1), /* pop saved return value into r1 */ a_rr(add, 1, 0), /* r0 += r1 */ a_ret, a_k16(lit16, 0, 1), /* r0 := 1 */ a_ret, }; int main(int argc, char **argv) { int n = argc > 1 ? atoi(argv[1]) : 6; u32 mem[1024]; for (int i = 0; i < 1024; i++) mem[i] = 0xdeafbeadu; memcpy(&mem[128], program, sizeof(program)); machine m = { .mem = mem }; for (int i = 0; i < 16; i++) m.reg[i] = 0xbadfadu; m.PC = 128; m.SP = 1024; int result = interpret(&m, n, 0xfeedbead, 0xfeedbead, 0xfeedbead); printf("%d\n", result); return 0; }