aboutsummaryrefslogtreecommitdiff
path: root/pu3/pu3.cpu.c
blob: 39d4a1dedb187923edd6c80602e7d134b5abc5d2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#define _POSIX_C_SOURCE 200809L
//

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>

// -- disable use of compiler intrinsics
// #define PU3_NO_INTRINSICS
// -- inaccurate clocks
// #define PU3_BAD_CLOCK

#ifndef PU3_BAD_CLOCK
clock_t ntime() {
    struct timespec t;
    clock_gettime(CLOCK_MONOTONIC_RAW, &t);

    return t.tv_nsec + t.tv_sec * 1e9;
}
#else
clock_t ntime() {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec * 1000000000 + tv.tv_usec * 1000;
}
#endif

// it is time
// the thing you've none been waiting for
// pu3

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 union {
    pu3_inst_t inst_struct;
    uint64_t   inst_num;
} pu3_inst_u;

typedef struct {
    pu3_inst_u *memory;
    uint32_t    mem_size;
    uint32_t   *pc;
    uint32_t   *time_slice;
} pu3_ctx;

// instructions
// 0: nop
// 1: halt
// 2: mov [target], src

pu3(pu3_ctx ctx) {
    static uint32_t   i;
    static pu3_inst_t cur;

    pu3_inst_u *pc = ctx.memory + *ctx.pc;

    static const __attribute__((aligned(64))) void *GOTO_TABLE[] = {
        &&inst_0,
        &&pu3_end,
        &&inst_2,
    };

pu3_start:
    cur = pc->inst_struct;
    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]

    // [5] [ ] [ ] [ ] [1] [2] [3] [4]
    goto *GOTO_TABLE[ cur.op & 0b1111 ];

inst_0: // nop
    pc += cur.dest;
    goto pu3_start;

inst_1: // halt
        // unused, we just jump to break

inst_2: // mov
    ctx.memory[ cur.dest ].inst_num = get_data();
    goto pu3_start;

pu3_end:
    *ctx.pc = pc - ctx.memory;
    return *ctx.time_slice - i;
}

const string op_names[] = { "nop", "halt", "mov" };

// 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;

        if (i == ctx.mem_size - 1) ctx.memory[ i ].inst_struct.op = 1;
    }
    ctx.memory[ i ].inst_struct.op = 0;
    *ctx.pc                        = 8;

    clock_t start = ntime();
    // int     wstart = rdtsc();
    pu3(ctx);
    // int     wend = rdtsc();
    clock_t end = ntime();

    double nanoseconds  = (end - start);
    double inst_count   = *ctx.pc - 8;
    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
}

main() {
    static const uint32_t SIZE = 1 << 24;

    pu3_ctx ctx = { .memory     = calloc(SIZE, sizeof(pu3_inst_u)),
                    .mem_size   = SIZE,
                    .pc         = calloc(1, sizeof(uint32_t)),
                    .time_slice = calloc(1, sizeof(uint32_t)) };

    bench_cycle(2, ctx);
}