diff options
Diffstat (limited to 'pu3')
| -rw-r--r-- | pu3/pu3.cpu.c | 269 |
1 files changed, 206 insertions, 63 deletions
diff --git a/pu3/pu3.cpu.c b/pu3/pu3.cpu.c index 39d4a1d..fe9838d 100644 --- a/pu3/pu3.cpu.c +++ b/pu3/pu3.cpu.c @@ -8,20 +8,23 @@ #include <time.h> #include <unistd.h> +#define auto __auto_type +#define let auto + // -- disable use of compiler intrinsics // #define PU3_NO_INTRINSICS // -- inaccurate clocks // #define PU3_BAD_CLOCK #ifndef PU3_BAD_CLOCK -clock_t ntime() { +uint64_t ntime() { struct timespec t; clock_gettime(CLOCK_MONOTONIC_RAW, &t); return t.tv_nsec + t.tv_sec * 1e9; } #else -clock_t ntime() { +uint64_t ntime() { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000000000 + tv.tv_usec * 1000; @@ -36,117 +39,257 @@ typedef char *string; typedef unsigned char byte; typedef byte bool; -typedef struct { - byte __spare[ 1 ]; - byte op; - uint16_t data; - uint32_t dest; -} pu3_inst_t; +typedef uint64_t pu3_inst_t; + +#ifndef PU3_NO_INTRINSICS + #define unlikely(JJ) __builtin_expect((JJ), 0) + #define likely(JJ) __builtin_expect((JJ), 1) +#else + #define unlikely(JJ) (JJ) + #define likely(JJ) (JJ) +#endif + +#define get_spare_nosh(__x) ((__x) & 0xFF00000000000000ULL) +#define get_scroll_byte_nosh(__x) ((__x) & 0x00FF000000000000ULL) +#define get_data_u16_nosh(__x) ((__x) & 0x0000FFFF00000000ULL) +#define get_dest_u32_nosh(__x) ((__x) & 0x00000000FFFFFFFFULL) +#define get_spare(__x) (get_spare_nosh(__x) >> 56) +#define get_scroll_byte(__x) (get_scroll_byte_nosh(__x) >> 48) +#define get_data_u16(__x) (get_data_u16_nosh(__x) >> 32) +#define get_dest_u32(__x) (get_dest_u32_nosh(__x) >> 0) +#define prep_spare(__x) (((__x) & 0x000000FFULL) << 56) +#define prep_scroll_byte(__x) (((__x) & 0x000000FFULL) << 48) +#define prep_data_u16(__x) (((__x) & 0x0000FFFFULL) << 32) +#define prep_dest_u32(__x) (((__x) & 0xFFFFFFFFULL) << 00) +#define set_spare(__v, __x) __v = (((__v) & 0x00FFFFFFFFFFFFFFULL) | prep_spare(__x)) +#define set_scroll_byte(__v, __x) __v = (((__v) & 0xFF00FFFFFFFFFFFFULL) | prep_scroll_byte(__x)) +#define set_data_u16(__v, __x) __v = (((__v) & 0xFFFF0000FFFFFFFFULL) | prep_data_u16(__x)) +#define set_dest_u32(__v, __x) __v = (((__v) & 0xFFFFFFFF00000000ULL) | prep_dest_u32(__x)) -typedef union { - pu3_inst_t inst_struct; - uint64_t inst_num; -} pu3_inst_u; +typedef struct { + pu3_inst_t *memory_u64; + uint32_t *memory_u32; +} pu3_mem; typedef struct { - pu3_inst_u *memory; - uint32_t mem_size; - uint32_t *pc; - uint32_t *time_slice; + pu3_mem memory; + + uint32_t mem_size; + uint32_t *pc; + uint32_t *time_slice; } pu3_ctx; +// instruct packing: +// typedef struct { +// byte __spare[ 1 ]; +// byte op; +// uint16_t data; +// uint32_t dest; +// } pu3_inst_t; // instructions -// 0: nop -// 1: halt -// 2: mov [target], src +// 0: nop +// 1: hlt +// 2: mov [target], src +// 3: add [target], src +// 4: sub [target], src +// 5: inc [target], ___ +// 6: dec [target], ___ +// 7: mul [target], src +// 8: lsh [target], src +// 9: rsh [target], src +// 10: and [target], src +// 11: not [target], src +// 12: jmp [target], src +// 13: jnz [target], src +// 14: ??? +// 15: ??? + +#define branchless(c, x, y) (((-(!!(c))) & (x)) | (~(-(!!(c))) & (y))) + +#define prep_raw() \ + ({ \ + rhs = (uint32_t) get_data_u16(cur); \ + if (get_spare(cur) & 0x40) rhs = ctx.memory.memory_u32[ rhs ]; \ + }) +#define prep_vals() \ + ({ \ + spare = get_spare(cur); \ + lhs = get_dest_u32(cur); \ + rhs = (uint32_t) get_data_u16(cur); \ + if (spare & 0x40) rhs = ctx.memory.memory_u32[ rhs ]; \ + if (unlikely(spare & 0x80)) { \ + out = lhs; \ + lhs = rhs; \ + rhs = out; \ + } \ + }) +#define prep_only_left() ({ lhs = get_dest_u32(cur); }) pu3(pu3_ctx ctx) { - static uint32_t i; - static pu3_inst_t cur; + uint64_t i = 0; + pu3_inst_t cur; - pu3_inst_u *pc = ctx.memory + *ctx.pc; + pu3_inst_t *pc = ctx.memory.memory_u64 + *ctx.pc; - static const __attribute__((aligned(64))) void *GOTO_TABLE[] = { - &&inst_0, - &&pu3_end, - &&inst_2, - }; + byte op; + byte spare; + // uint32_t lhs; + // uint32_t rhs; + uint32_t lhs; + uint32_t rhs; + uint32_t out; -pu3_start: - cur = pc->inst_struct; - pc++; + const __attribute__((aligned(64))) void *goto_table[] + = { &&pu3_inst_0, &&pu3_inst_1, &&pu3_inst_2, &&pu3_inst_3, &&pu3_inst_4, &&pu3_inst_5, + &&pu3_inst_6, &&pu3_inst_7, &&pu3_inst_8, &&pu3_inst_9, &&pu3_inst_10, &&pu3_inst_11, + &&pu3_inst_12, &&pu3_inst_13, &&pu3_inst_14, &&pu3_inst_15 }; +pu3_start: + i++; + cur = *pc++; #ifndef PU3_NO_INTRINSICS __builtin_prefetch(pc + 1, 0, 3); #endif -#define get_data() ((cur.op & 0b10000000) ? (ctx.memory[ cur.data ].inst_num) : (cur.data)) - // 1 - 4: opcode - // 5: data_type [0 = constant, 1 = memory] + op = get_scroll_byte(cur); + + goto *goto_table[ op & 0b1111 ]; + +pu3_inst_0: // nop + prep_raw(); + pc += rhs; + goto pu3_start; + +pu3_inst_2: // mov + prep_vals(); + ctx.memory.memory_u32[ lhs ] = rhs; + goto pu3_start; + +pu3_inst_3: // add + prep_vals(); + ctx.memory.memory_u32[ lhs ] += rhs; + goto pu3_start; + +pu3_inst_4: // sub + prep_vals(); + ctx.memory.memory_u32[ lhs ] -= rhs; + goto pu3_start; + +pu3_inst_5: // inc + prep_only_left(); + ctx.memory.memory_u32[ lhs ]++; + goto pu3_start; + +pu3_inst_6: // dec + prep_only_left(); + ctx.memory.memory_u32[ lhs ]--; + goto pu3_start; + +pu3_inst_7: // mul + prep_vals(); + ctx.memory.memory_u32[ lhs ] *= rhs; + goto pu3_start; + +pu3_inst_8: // lsh + prep_vals(); + ctx.memory.memory_u32[ lhs ] <<= rhs; + goto pu3_start; + +pu3_inst_9: // rsh + prep_vals(); + ctx.memory.memory_u32[ lhs ] >>= rhs; + goto pu3_start; - // [5] [ ] [ ] [ ] [1] [2] [3] [4] - goto *GOTO_TABLE[ cur.op & 0b1111 ]; +pu3_inst_10: // and + prep_vals(); + ctx.memory.memory_u32[ lhs ] &= rhs; + goto pu3_start; -inst_0: // nop - pc += cur.dest; +pu3_inst_11: // not + prep_vals(); + ctx.memory.memory_u32[ lhs ] = !rhs; goto pu3_start; -inst_1: // halt - // unused, we just jump to break +pu3_inst_12: // jmp + prep_only_left(); + pc = ctx.memory.memory_u64 + lhs; + goto pu3_start; -inst_2: // mov - ctx.memory[ cur.dest ].inst_num = get_data(); +pu3_inst_13: // jnz + prep_vals(); + if (rhs) pc = ctx.memory.memory_u64 + lhs; goto pu3_start; -pu3_end: - *ctx.pc = pc - ctx.memory; +pu3_inst_14: // unused +pu3_inst_15: // unused + +pu3_inst_1: // hlt + *ctx.pc = pc - ctx.memory.memory_u64; return *ctx.time_slice - i; } -const string op_names[] = { "nop", "halt", "mov" }; +const string op_names[] = { "nop", "hlt", "mov", "add", "sub", "inc", "dec", "mul", + "lsh", "rsh", "and", "not", "jmp", "jnz", "???", "???" }; // bench: measure using clock() double bench_cycle(byte instr, pu3_ctx ctx) { int i; - for (i = 16; i < ctx.mem_size; i++) { - ctx.memory[ i ].inst_struct.op = instr & 0b1111; - ctx.memory[ i ].inst_struct.dest = 5; - ctx.memory[ i ].inst_struct.data = 6; + for (i = 16; i < ctx.mem_size - 16; i++) { + set_scroll_byte(ctx.memory.memory_u64[ i ], instr & 0b1111); + set_data_u16(ctx.memory.memory_u64[ i ], 2); + set_dest_u32(ctx.memory.memory_u64[ i ], 4); - if (i == ctx.mem_size - 1) ctx.memory[ i ].inst_struct.op = 1; + if (i == ctx.mem_size - 20) set_scroll_byte(ctx.memory.memory_u64[ i ], 1); } - ctx.memory[ i ].inst_struct.op = 0; - *ctx.pc = 8; + set_scroll_byte(ctx.memory.memory_u64[ i ], 0); + *ctx.pc = 8; - clock_t start = ntime(); - // int wstart = rdtsc(); + uint64_t start = ntime(); pu3(ctx); - // int wend = rdtsc(); - clock_t end = ntime(); + uint64_t end = ntime(); - double nanoseconds = (end - start); + double nanoseconds = (double) (end - start); double inst_count = *ctx.pc - 8; double insts_per_ns = inst_count / nanoseconds; - double insts_per_s = insts_per_ns * 1e9; + double insts_per_s = insts_per_ns * 1000000000ULL; printf( - "%-8s | time: %.3fms | insts: %.0f (%li real) | ips: %.3fM inst/s\n", + "%-8s | time: %.3fms | insts: %.0f (%llu real) | ips: %.3fM inst/s\n", op_names[ instr ], nanoseconds / 1000000, inst_count, - end - start, + (unsigned long long) (end - start), insts_per_ns * 1e3); return insts_per_s / 1e6; // return M inst/s } -main() { - static const uint32_t SIZE = 1 << 24; +int main() { + const uint32_t SIZE = 1 << 24; - pu3_ctx ctx = { .memory = calloc(SIZE, sizeof(pu3_inst_u)), + let mem_arr = calloc(SIZE, sizeof(pu3_inst_t)); + + pu3_ctx ctx = { .memory = { .memory_u64 = mem_arr, .memory_u32 = mem_arr }, .mem_size = SIZE, .pc = calloc(1, sizeof(uint32_t)), .time_slice = calloc(1, sizeof(uint32_t)) }; - bench_cycle(2, ctx); + int i; + int total = 0; + double benchs = 0; + for (i = 0; i < 12; i++) { + if (i == 1) continue; + total++; + benchs += bench_cycle(i, ctx); + } + + printf( + "-------------------------------\n" + "TOTAL: %fMHz\n", + benchs / total); + + free(mem_arr); + free(ctx.pc); + free(ctx.time_slice); + return 0; }
\ No newline at end of file |
