]> git.sesse.net Git - interpreter_trials/commitdiff
Add a tail-call variant with state as global variables in locked registers. main
authorSteinar H. Gunderson <steinar+nageru@gunderson.no>
Wed, 23 Jun 2021 07:45:29 +0000 (09:45 +0200)
committerSteinar H. Gunderson <steinar+nageru@gunderson.no>
Wed, 23 Jun 2021 07:45:40 +0000 (09:45 +0200)
interpreter.h
interpreter12.cpp [new file with mode: 0644]
interpreter_trials.cpp
meson.build

index dbe1ba1c9f03e028e2a190c10dbc1d38bd204ad9..144d4e3bd09187512953f37e83a4a9cb3fba0236 100644 (file)
@@ -59,4 +59,9 @@ namespace interpreter11
     std::pair<int32_t, uint32_t> interpreter_run(uint8_t *program, int32_t param);
 }
 
+namespace interpreter12
+{
+    std::pair<int32_t, uint32_t> interpreter_run(uint8_t *program, int32_t param);
+}
+
 #endif
diff --git a/interpreter12.cpp b/interpreter12.cpp
new file mode 100644 (file)
index 0000000..87851c2
--- /dev/null
@@ -0,0 +1,174 @@
+#include "bytecode.h"
+#include <utility>
+#include "interpreter.h"
+
+namespace interpreter12
+{
+
+    struct Flags
+    {
+        unsigned int zero:16;
+        unsigned int negative:16;
+    };
+
+    struct CpuState
+    {
+        int32_t regs[16] = {0};
+        uint32_t final_cycle_count = 0;
+    };
+
+#ifdef __arm__
+register CpuState *state asm("r4");
+register uint8_t *pc asm("r5");
+register Flags flags asm("r6");
+register uint32_t cycle_count asm("r7");
+#elif __x86_64__
+register CpuState *state asm("r12");
+register uint8_t *pc asm("r13");
+register Flags flags asm("r14");
+register uint32_t cycle_count asm("rbx");
+#else
+static CpuState *state;
+static uint8_t *pc;
+static Flags flags;
+static uint32_t cycle_count;
+#endif
+
+    extern void (*dispatch_table[])();
+
+    void op_return()
+    {
+        cycle_count += 1;
+        state->final_cycle_count = cycle_count;
+        return;
+    }
+
+    void op_add()
+    {
+        uint8_t reg = *pc++;
+        uint8_t dest = reg>>4, src = reg & 0xf;
+
+        int32_t v = state->regs[dest];
+        v += state->regs[src];
+
+        flags.zero = (v == 0);
+        flags.negative = (v < 0);
+        state->regs[dest] = v;
+        cycle_count += 2;
+
+        uint8_t opcode = *pc++;
+        dispatch_table[opcode]();
+    }
+
+    void op_sub()
+    {
+        uint8_t reg = *pc++;
+        uint8_t dest = reg>>4, src = reg & 0xf;
+
+        int32_t v = state->regs[dest];
+        v -= state->regs[src];
+
+        flags.zero = (v == 0);
+        flags.negative = (v < 0);
+        state->regs[dest] = v;
+        cycle_count += 2;
+
+        uint8_t opcode = *pc++;
+        dispatch_table[opcode]();
+    }
+
+    void op_mov()
+    {
+        uint8_t reg = *pc++;
+        uint8_t dest = reg>>4, src = reg & 0xf;
+
+        state->regs[dest] = state->regs[src];
+        cycle_count += 2;
+
+        uint8_t opcode = *pc++;
+        dispatch_table[opcode]();
+    }
+
+    void op_movi()
+    {
+        uint8_t reg = *pc++;
+        uint8_t dest = reg>>4;
+
+        int32_t imm = *(int32_t *)pc;
+        pc += 4;
+
+        state->regs[dest] = imm;
+        cycle_count += 6;
+
+        uint8_t opcode = *pc++;
+        dispatch_table[opcode]();
+    }
+
+    void op_b()
+    {
+        int8_t rel = *pc++;
+        pc += rel;
+        cycle_count += 2;
+
+        uint8_t opcode = *pc++;
+        dispatch_table[opcode]();
+    }
+
+    void op_bnz()
+    {
+        int8_t rel = *pc++;
+        if(!flags.zero)
+        {
+            pc += rel;
+        }
+        cycle_count += 2;
+
+        uint8_t opcode = *pc++;
+        dispatch_table[opcode]();
+    }
+
+    void (*dispatch_table[])() =
+    {
+        op_return,
+        op_add,
+        op_sub,
+        op_mov,
+        op_movi,
+        op_b,
+        op_bnz
+    };
+
+    std::pair<int32_t, uint32_t> interpreter_run(uint8_t *program, int32_t param)
+    {
+        // Save the previous value of our locked registers.
+        CpuState *save_state = state;
+        uint8_t *save_pc = pc;
+        Flags save_flags = {flags.zero, flags.negative};
+        uint32_t save_cycle_count = cycle_count;
+
+        CpuState local_state;
+        state = &local_state;
+        pc = program;
+        state->regs[X0] = param;
+        cycle_count = 0;
+        flags.zero = flags.negative = 0;
+
+        uint8_t opcode = *pc++;
+        dispatch_table[opcode]();
+
+        auto ret = std::make_pair(state->regs[X0], state->final_cycle_count);
+
+        // Restore locked registers. (Note that on 64-bit platforms, this will
+        // corrupt the high 32 bits of flags and cycle_count, so this is really
+        // only safe on 32-bit as it stands.)
+        state = save_state;
+        pc = save_pc;
+        flags.zero = save_flags.zero;
+        flags.negative = save_flags.negative;
+        cycle_count = save_cycle_count;
+
+        return ret;
+    }
+
+
+}
index ece0c0514094db835c0de4a28aa688138d9d06c3..3ce416d097dc304f24633655a297e90b73938e1d 100644 (file)
@@ -108,4 +108,12 @@ static void interpreter11_computed_goto(benchmark::State& state) {
 }
 BENCHMARK(interpreter11_computed_goto);
 
+static void interpreter12_tail_calls_locked_reg(benchmark::State& state) {
+    for (auto _ : state)
+    {
+        run(interpreter12::interpreter_run);
+    }
+}
+BENCHMARK(interpreter12_tail_calls_locked_reg);
+
 BENCHMARK_MAIN();
index 9c1155a10ab0853fe999c64bce2e6efd06a80a6d..552b9b9b0fa7367a244eed6f31b9275ac7e0bc48 100644 (file)
@@ -18,4 +18,5 @@ executable('interpreter_trials',
        'interpreter9.cpp',
        'interpreter10.cpp',
        'interpreter11.cpp',
+       'interpreter12.cpp',
                                        dependencies : benchmark_dep)