aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Moon Flower <moon@coffin.ir> 2025-08-27 19:11:02 +0330
committerGravatar Moon Flower <moon@coffin.ir> 2025-08-27 19:11:02 +0330
commit7e6fa202ef4e2e590f614f6c1c0cceeb290b41ab (patch)
treed3229b314888ef7d9a5f2600a5f1aa1e918cba0a
parentpu3 poc (diff)
pu3 poc2
-rw-r--r--pu3/pu3.cpu.c269
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