aboutsummaryrefslogtreecommitdiff
path: root/pu2/pu2.c
diff options
context:
space:
mode:
authorGravatar Moon Flower <moon@coffin.ir> 2025-08-19 12:15:10 +0330
committerGravatar Moon Flower <moon@coffin.ir> 2025-08-19 12:15:10 +0330
commitde25a0291820dcb953dfdc3f13241bb3cb56e56b (patch)
treedca89e2bfc06b0da08a63f2aa68920558c07c43d /pu2/pu2.c
parentremove ppu from readme (diff)
improved pu2 (vm speed ~1GHz on 1.8GHz cpu), reduced code emission, added another compile flag
Diffstat (limited to 'pu2/pu2.c')
-rw-r--r--pu2/pu2.c247
1 files changed, 150 insertions, 97 deletions
diff --git a/pu2/pu2.c b/pu2/pu2.c
index 6121d8b..d1f6fe1 100644
--- a/pu2/pu2.c
+++ b/pu2/pu2.c
@@ -1,7 +1,13 @@
#define _POSIX_C_SOURCE 200809L
#include "pu2.out.c"
+//
+#include <errno.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
#include <time.h>
+#include <unistd.h>
typedef char *string;
@@ -87,7 +93,7 @@ long powi(long x, unsigned n) {
p *= p, n /= 2;
}
- return r;
+ return p == 0 ? 0 : r;
}
emu() {
@@ -163,9 +169,9 @@ skip_space(string *I) {
// IR parser for pu2's as2
// clang-format off
struct {
- int hlt, imm, lod, str, swp, add, sub, and, not, acc, a2u, u2a, b2u, u2b;
+ int hlt, imm, lod, str, swp, add, sub, and, not, acc, a2u, u2a, b2u, u2b, nop, cb1, cb2, cb3, cb4;
} opcode = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18
};
// clang-format on
@@ -194,16 +200,6 @@ clean_slice_ref(slice *val) { free(val->ref); }
#define static_len(G) (sizeof(G) / sizeof((G)[ 0 ]))
-#define emit(OP, VAL) \
- ({ \
- *Code++ = (OP); \
- *Code++ = (VAL); \
- })
-#define store() \
- ({ \
- emit(opcode.acc, 1); \
- emit(opcode.str, 0); \
- })
#define run_err(result, ...) \
({ \
ERR_code = init_I; \
@@ -262,34 +258,15 @@ clean_slice_ref(slice *val) { free(val->ref); }
value; \
})
-#define op(mode) emit(opcode.mode, 0)
-#define swap() emit(opcode.swp, 0)
-#define imm(value) emit(opcode.imm, value)
-#define load() emit(opcode.lod, 0)
-#define imm_in_mem(__mem, __imm) \
- ({ \
- imm(__imm); \
- swap(); \
- imm(__mem); \
- store(); \
- })
-#define mov_mem_mem(__mem1, __mem2) \
- ({ \
- imm(__mem2); \
- swap(); \
- load(); \
- swap(); \
- imm(__mem1); \
- store(); \
- })
-#define mem_in_a(__mem) \
- ({ \
- op(b2u); \
- imm(__mem); \
- swap(); \
- load(); \
- op(u2b); \
- })
+// #define emit(OP, VAL) (*Code++ = (OP), *Code++ = (VAL))
+#define store() (emit(opcode.acc, 1), emit(opcode.str, 0))
+#define op(mode) emit(opcode.mode, 0)
+#define swap() emit(opcode.swp, 0)
+#define imm(value) emit(opcode.imm, value)
+#define load() emit(opcode.lod, 0)
+#define imm_in_mem(__mem, __imm) (imm(__mem), emit(opcode.cb3, __imm))
+#define mov_mem_mem(__mem1, __mem2) (imm(__mem1), emit(opcode.cb4, __mem2))
+#define mem_in_a(__mem) emit(opcode.cb2, __mem)
#define perform_op(operation) \
({ \
mem_in_a(mem2_index); \
@@ -306,6 +283,13 @@ string ERR;
string ERR_code;
int *Code = &m[ 4000 ];
int *Data = &m[ static_len(m) - 1 ]; // grows downwards
+
+__attribute__((always_inline)) emit(int op, int val) {
+ *Code++ = op;
+ *Code++ = val;
+}
+
+bool has_jump_warned = false;
_ir2(string I, macro macros[ 1 << 9 ]) {
string init_I = I;
ERR = "all good";
@@ -328,6 +312,12 @@ _ir2(string I, macro macros[ 1 << 9 ]) {
checkskip('=', "expected assigment after memory blob (hint: only +-&! are supported)");
skip_space(&I);
+ if (mem_index == 7 && !has_jump_warned) {
+ has_jump_warned = true;
+ printf("\nwarn: writing to memory index 7 is equivalent to a jump\n");
+ printf(" if this behavior is intended, use the jump blob instead.\n\n");
+ }
+
if (*I == '@') {
I++;
if (!isdigit(*I)) { run_err(I, "expected a digit after @"); }
@@ -360,6 +350,8 @@ _ir2(string I, macro macros[ 1 << 9 ]) {
swap();
mem_in_a(mem_index);
emit(opcode.add, 0);
+ swap();
+ imm(mem_index);
store();
} else if (extra_op == '-') {
imm(imm);
@@ -633,12 +625,12 @@ _ir2(string I, macro macros[ 1 << 9 ]) {
}
ir2(string I) {
- macro macros[ 1 << 9 ] = {0};
+ macro macros[ 1 << 9 ] = { 0 };
_ir2(I, macros);
}
-string op_names[] = { "hlt", "imm", "lod", "str", "swp", "add", "sub",
- "and", "not", "acc", "a2u", "u2a", "b2u", "u2b" };
+string op_names[] = { "hlt", "imm", "lod", "str", "swp", "add", "sub", "and", "not", "acc",
+ "a2u", "u2a", "b2u", "u2b", "nop", "cb1", "cb2", "cb3", "cb4" };
unsigned long long rdtsc(void) {
unsigned hi, lo;
@@ -646,6 +638,14 @@ unsigned long long rdtsc(void) {
return ((unsigned long long) hi << 32) | lo;
}
+clock_t ntime() {
+ struct timespec t;
+ clock_gettime(CLOCK_MONOTONIC_RAW, &t);
+
+ return t.tv_nsec + t.tv_sec * 1e9;
+}
+
+// bench: measure using clock()
double bench_cycle(int instr) {
int i;
for (i = 0; i < S - 2; i += 2) {
@@ -658,15 +658,29 @@ double bench_cycle(int instr) {
m[ 7 ] = 0;
c = 0;
- clock_t start = clock();
+ clock_t start = ntime();
+ size_t wstart = rdtsc();
pu2();
- clock_t end = clock();
-
- double seconds = (double) (end - start) / CLOCKS_PER_SEC;
- printf("time: %.6f s\n", seconds);
- return (double) m[ 7 ] / seconds / 1e6;
+ size_t wend = rdtsc();
+ clock_t end = ntime();
+
+ double nanoseconds = (end - start);
+ double inst_count = (double) m[ 7 ] / 2;
+ double insts_per_ns = inst_count / nanoseconds;
+ double insts_per_s = insts_per_ns * 1e9;
+
+ printf(
+ "%-8s | time: %.3fms | insts: %.0f (%li real) | ips: %.3fM inst/s\n",
+ op_names[ instr ],
+ nanoseconds / 1000000,
+ inst_count,
+ end - start,
+ insts_per_ns * 1e3);
+
+ return insts_per_s / 1e6; // return M inst/s
}
+// bench2: measure using rdtsc
double bench2_cycle(int instr) {
unsigned long long start, end;
double vminst_count, cpu_cycles;
@@ -681,58 +695,116 @@ double bench2_cycle(int instr) {
c = 0;
start = rdtsc();
pu2();
- end = rdtsc();
- cpu_cycles = (double) (end - start);
- vminst_count = (double) m[ 7 ];
- printf("pu2/real: %f\n", cpu_cycles / vminst_count);
- return cpu_cycles / vminst_count;
+ end = rdtsc();
+ cpu_cycles = (double) (end - start);
+ vminst_count = (double) m[ 7 ] / 2;
+ double cycles_per_inst = cpu_cycles / vminst_count;
+
+ printf(
+ "%-6s | cycles/inst: %8.3f | cycles: %14.0f | insts: %12.0f\n",
+ op_names[ instr ],
+ cycles_per_inst,
+ cpu_cycles,
+ vminst_count);
+ return cycles_per_inst;
+}
+
+sleep_nano(int nanoseconds) {
+ struct timespec req = { 0 };
+ req.tv_sec = nanoseconds / 1000000000L; // Whole seconds
+ req.tv_nsec = nanoseconds % 1000000000L; // Remaining nanoseconds
+
+ // nanosleep might be interrupted, so loop until it completes
+ while (nanosleep(&req, &req) == -1 && errno == EINTR) {
+ continue; // Continue with remaining time if interrupted
+ }
}
-benchmark(double (*target)(int)) {
- double results[ 13 ];
+[[clang::always_inline]] sleep_micro(int microseconds) { sleep_nano(microseconds * 1e3); }
+[[clang::always_inline]] sleep_milli(int milliseconds) { sleep_nano(milliseconds * 1e6); }
+
+// benchmark: run target for all opcodes and print summary
+benchmark(double (*target)(int), const char *mode_label) {
+ double results[ 19 ];
double sum = 0;
int count = 0;
int i;
- for (i = 1; i < 14; i++) {
- printf("running: %s\n", op_names[ i ]);
+
+ printf("\n=== pu2 benchmark (%s) ===\n", mode_label);
+ printf("op | result details\n");
+ printf("---------------------------------------------\n");
+
+ for (i = 1; i < 19; i++) {
+ // printf("running: %s\n", op_names[ i ]);
results[ i ] = target(i);
sum += results[ i ];
count++;
}
sum /= count;
- unsigned long long start, end;
- start = rdtsc();
- sleep(1);
- end = rdtsc();
-
- float clock_speed = (end - start) / 1e6;
- printf("results sum: %f\n", sum);
- if (target == bench_cycle) {
- printf("clock speed ~ %.6f MHz\n", clock_speed);
- printf("instructions per cycle: %f\n", clock_speed / sum);
+ printf("---------------------------------------------\n");
+
+ size_t start = rdtsc();
+ sleep_milli(100);
+ size_t end = rdtsc();
+
+ double clock_speed_mhz = ((end - start) / 1e6) * 10;
+
+ if (strcmp(mode_label, "time") == 0) {
+ // printf("average : %.3f M inst/s\n", sum);
+ printf("cpu clock : ~%.3f MHz (measured via rdtsc & msleep(100))\n", clock_speed_mhz);
+ printf("vm clock (i/s) : %.3fMHz (ipn, higher is better)\n", sum);
+ // printf("estimated : %.3f cycles per instruction (cpi, lower is better)\n",
+ // clock_speed_mhz / sum);
+ } else {
+ // mode_label == "rdtsc"
+ printf("average cycles/inst : %.3f cycles\n", sum);
+ // printf("cpu clock : ~%.3f MHz (measured via rdtsc & msleep(100))\n",
+ // clock_speed_mhz);
+ printf("estimated ipc : %.3f (1 / cycles_per_inst, higher is better)\n", 1.0 / sum);
+ // printf("estimated cpi : %.3f (cycles_per_inst, lower is better)\n", sum);
}
+ printf("=== end benchmark ===\n\n");
}
-long nstime() {
+nstime() {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_nsec;
}
+volatile long out;
+
main(int argc, char **argv) {
if (argc == 1) {
return printf(
"\nusage:\n"
" %s <file_name> open and compile ir2-compliant code\n"
- " %s --bench benchmark pu2\n\n",
+ " %s benchmark benchmark pu2 (two modes: time and rdtsc)\n\n",
argv[ 0 ],
argv[ 0 ]);
}
- if (strcmp(argv[ 1 ], "--bench") == 0) {
- benchmark(bench_cycle);
- benchmark(bench2_cycle);
+ if (strcmp(argv[ 1 ], "benchmark") == 0) {
+ benchmark(bench_cycle, "time");
+
+ const long N = S / 2; // printably the same insts you report
+ long sum = 0;
+ unsigned aux;
+ unsigned long long t0 = rdtsc();
+ int C;
+ for (C = 0; C < N; ++C) {
+ sum += 1;
+ __asm__ volatile("");
+ }
+ unsigned long long t1 = rdtsc();
+ out = sum; // single volatile write to make sum observable
+ printf(
+ "N=%llu cycles=%llu cycles/iter=%.6f\n",
+ (unsigned long long) N,
+ (unsigned long long) (t1 - t0),
+ (t1 - t0) / (double) N);
+ // benchmark(bench2_cycle, "rdtsc");
exit(0);
}
@@ -745,35 +817,16 @@ main(int argc, char **argv) {
}
size_t o_size;
- long before = nstime();
- long cycles_bef = rdtsc();
+ long before = nstime();
+ long cycles_bef = rdtsc();
ir2(contents);
long cycles_aft = rdtsc();
- long after = nstime();
- m[ 7 ] = 4000;
+ long after = nstime();
+ m[ 7 ] = 4000;
- // fprintf(stderr, "assembly took %li nanoseconds (%li cycles)\n", (after - before), (cycles_aft - cycles_bef));
free(contents);
size_t res_size = Code - &m[ 4000 ];
- // --- BYTES PRINTING ---
-
- // for(i = 0; i < res_size; i++) {
- // printf("%X%X ", m[4000+i]>>4, m[4000+i]&0b1111);
- // if(i % 8 == 7) printf("\n");
- // }
- // fflush(stdout);
- // printf("\n\n");
-
- // --- OPCODES PRINTING ---
-
- // for (i = 0; i < res_size; i++) {
- // int op = m[ 4000 + i++ ];
- // int data = m[ 4000 + i ];
-
- // printf("%s(%i);\n", op_names[ op ], data);
- // }
- // printf("\n\n");
emu();
}