diff options
Diffstat (limited to 'arch')
| -rw-r--r-- | arch/null/arch.c | 241 | ||||
| -rw-r--r-- | arch/null/arch_inst.c | 48 | ||||
| -rw-r--r-- | arch/null/arch_spec.h | 19 | ||||
| -rw-r--r-- | arch/null/client_plots.h | 32 | ||||
| -rw-r--r-- | arch/v1/arch.c | 989 | ||||
| -rw-r--r-- | arch/v1/arch_inst.c | 40 | ||||
| -rw-r--r-- | arch/v1/arch_spec.h | 140 | ||||
| -rw-r--r-- | arch/v1/client_plots.h | 83 |
8 files changed, 1592 insertions, 0 deletions
diff --git a/arch/null/arch.c b/arch/null/arch.c new file mode 100644 index 0000000..f9e7ef4 --- /dev/null +++ b/arch/null/arch.c @@ -0,0 +1,241 @@ +// file : arch/null/arch.c +// project : Salis-VM +// author : Paul Oliver <contact@pauloliver.dev> +// +// Example of a minimal viable VM architecture. Useful for testing. +// Also works as a placeholder when creating new VM architectures. + +// index: +// [section] includes +// [section] getters +// [section] callbacks +// [section] validation +// [section] data +// [section] main + +// ---------------------------------------------------------------------------- +// [section] includes +// ---------------------------------------------------------------------------- +#include <assert.h> +#include <sqlite3.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <threads.h> +#include <zlib.h> + +#include "arch.h" +#include "arch_spec.h" +#include "arch_inst.h" +#include "compress.h" +#include "logger.h" +#include "salis.h" +#include "sql.h" + +// ---------------------------------------------------------------------------- +// [section] getters +// ---------------------------------------------------------------------------- +uint64_t arch_proc_get_ip_addr(const struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + return proc_get(core, pix)->ip; +} + +uint64_t arch_proc_get_sp_addr(const struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + return proc_get(core, pix)->sp; +} + +uint64_t arch_proc_get_mb0_addr(const struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + return proc_get(core, pix)->mb0a; +} + +uint64_t arch_proc_get_mb0_size(const struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + return proc_get(core, pix)->mb0s; +} + +uint64_t arch_proc_get_mb1_addr(const struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + return proc_get(core, pix)->mb1a; +} + +uint64_t arch_proc_get_mb1_size(const struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + return proc_get(core, pix)->mb1s; +} + +uint64_t arch_proc_get_slice(const struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + (void)core; + (void)pix; + return 1; +} + +// ---------------------------------------------------------------------------- +// [section] callbacks +// ---------------------------------------------------------------------------- +void arch_on_proc_step(struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + (void)core; + (void)pix; + return; +} + +void arch_on_proc_kill(struct Core *core) { + assert(core); + assert(core->pnum > 1); + (void)core; +} + +// ---------------------------------------------------------------------------- +// [section] validation +// ---------------------------------------------------------------------------- +#if !defined(NDEBUG) +void arch_validate_core(struct Core *core) { + assert(core); + (void)core; +} +#endif + +// ---------------------------------------------------------------------------- +// [section] data +// ---------------------------------------------------------------------------- +void arch_push_data_header(void) { + log_info("Creating arch table in SQLite database"); + sql_exec( + NULL, NULL, + "create table arch (" +#define FOR_CORE(i) \ + "null_0_pop_" #i " int not null, " \ + "null_1_pop_" #i " int not null, " \ + "null_2_pop_" #i " int not null, " \ + "null_3_pop_" #i " int not null, " + FOR_CORES +#undef FOR_CORE + "step int not null" + ");" + ); +} + +static int arch_count_inst_pop(struct Core *core) { + assert(core); + + for (uint64_t i = 0; i < MVEC_SIZE; i++) { + core->ipop[mvec_get_inst(core, i)]++; + } + +#if !defined(NDEBUG) + uint64_t pop_total = 0; + + for (uint64_t i = 0; i < ARCH_INST_COUNT; i++) { + pop_total += core->ipop[i]; + } + + assert(pop_total == MVEC_SIZE); +#endif + + return 0; +} + +void arch_prepare_data_line(void) { + assert(g_sim_db); + + // Measure instruction population in parallel + for (int i = 0; i < CORES; i++) { + struct Core *core = &g_cores[i]; + thrd_create(&core->ipop_thrd, (thrd_start_t)arch_count_inst_pop, core); + } + + for (int i = 0; i < CORES; i++) { + struct Core *core = &g_cores[i]; + thrd_join(core->ipop_thrd, NULL); + } +} + +void arch_push_data_line(FILE *eva_file) { + assert(eva_file); + (void)eva_file; + log_info("Pushing row to arch table in SQLite database"); + sql_exec( + NULL, NULL, + "insert into arch (" +#define FOR_CORE(i) \ + "null_0_pop_" #i ", " \ + "null_1_pop_" #i ", " \ + "null_2_pop_" #i ", " \ + "null_3_pop_" #i ", " + FOR_CORES +#undef FOR_CORE + "step" + ") values (" +#define FOR_CORE(i) \ + "%ld, %ld, %ld, %ld, " + FOR_CORES +#undef FOR_CORE + "%ld" + ");", +#define FOR_CORE(i) \ + g_cores[i].ipop[0], \ + g_cores[i].ipop[1], \ + g_cores[i].ipop[2], \ + g_cores[i].ipop[3], + FOR_CORES +#undef FOR_CORE + g_step + ); + + // Reset data aggregation fields + for (int i = 0; i < CORES; i++) { + struct Core *core = &g_cores[i]; + memset(core->ipop, 0, sizeof(uint64_t) * ARCH_INST_COUNT); + } +} + +// ---------------------------------------------------------------------------- +// [section] main +// ---------------------------------------------------------------------------- +#if defined(COMMAND_NEW) +void arch_core_init(struct Core *core) { + assert(core); + + for (uint64_t i = 0; i < CLONES; i++) { + uint64_t addr_clone = (MVEC_SIZE / CLONES) * i; + struct Proc *panc = proc_fetch(core, i); + panc->mb0a = addr_clone; + panc->mb0s = ANC_SIZE; + panc->ip = addr_clone; + panc->sp = addr_clone; + } +} +#endif + +#if defined(COMMAND_LOAD) +void arch_core_load(struct Core *core, FILE *f) { + assert(core); + assert(f); + (void)core; + (void)f; +} +#endif + +void arch_core_save(const struct Core *core, FILE *f) { + assert(core); + assert(f); + (void)core; + (void)f; +} + +void arch_core_free(struct Core *core) { + assert(core); + (void)core; +} diff --git a/arch/null/arch_inst.c b/arch/null/arch_inst.c new file mode 100644 index 0000000..4744382 --- /dev/null +++ b/arch/null/arch_inst.c @@ -0,0 +1,48 @@ +// file : arch/null/arch_inst.c +// project : Salis-VM +// author : Paul Oliver <contact@pauloliver.dev> + +#include <assert.h> +#include <stdbool.h> +#include <stdint.h> +#include <wchar.h> + +#define ARCH_EXPORT_INST_COUNT +#include "arch_spec.h" +#include "arch_inst.h" + +wchar_t arch_inst_symbol(uint8_t inst) { + assert(inst < ARCH_INST_COUNT); + + switch (inst) { + case 0: + return L'⢀'; + case 1: + return L'⢁'; + case 2: + return L'⢂'; + case 3: + return L'⢃'; + } + + assert(false); + return L'\0'; +} + +const char *arch_inst_mnemonic(uint8_t inst) { + assert(inst < ARCH_INST_COUNT); + + switch (inst) { + case 0: + return "null 0"; + case 1: + return "null 1"; + case 2: + return "null 2"; + case 3: + return "null 3"; + } + + assert(false); + return NULL; +} diff --git a/arch/null/arch_spec.h b/arch/null/arch_spec.h new file mode 100644 index 0000000..466e69f --- /dev/null +++ b/arch/null/arch_spec.h @@ -0,0 +1,19 @@ +// file : arch/null/arch_spec.h +// project : Salis-VM +// author : Paul Oliver <contact@pauloliver.dev> + +#pragma once + +#define ARCH_SPEC_INST_BITS 2 + +#define ARCH_SPEC_CORE_FIELDS \ + ARCH_SPEC_CORE_FIELD(thrd_t, ipop_thrd) \ + ARCH_SPEC_CORE_FIELD(uint64_t, ipop[ARCH_INST_COUNT]) \ + +#define ARCH_SPEC_PROC_FIELDS \ + ARCH_SPEC_PROC_FIELD(uint64_t, ip) \ + ARCH_SPEC_PROC_FIELD(uint64_t, sp) \ + ARCH_SPEC_PROC_FIELD(uint64_t, mb0a) \ + ARCH_SPEC_PROC_FIELD(uint64_t, mb0s) \ + ARCH_SPEC_PROC_FIELD(uint64_t, mb1a) \ + ARCH_SPEC_PROC_FIELD(uint64_t, mb1s) diff --git a/arch/null/client_plots.h b/arch/null/client_plots.h new file mode 100644 index 0000000..72cd052 --- /dev/null +++ b/arch/null/client_plots.h @@ -0,0 +1,32 @@ +// file : arch/null/client_plots.h +// project : Salis-VM +// author : Paul Oliver <contact@pauloliver.dev> + +#pragma once + +static std::array g_arch_traces = std::to_array<TraceNamed<ImS64>>({ +#define FOR_CORE(i) \ + {"null_0_pop_" #i, "null_0_pop_" #i}, \ + {"null_1_pop_" #i, "null_1_pop_" #i}, \ + {"null_2_pop_" #i, "null_2_pop_" #i}, \ + {"null_3_pop_" #i, "null_3_pop_" #i}, + FOR_CORES +#undef FOR_CORE +}); + +static std::array<TraceHeatmap<ImS64>, 0> g_arch_traces_heatmaps = {}; +static std::array<PlotLines, 0> g_arch_plots = {}; + +static std::array g_arch_plots_stacked = std::to_array<PlotStacked>({ +#define FOR_CORE(i) \ + {"ipop%_" #i, "population", { \ + "null_0_pop_" #i, \ + "null_1_pop_" #i, \ + "null_2_pop_" #i, \ + "null_3_pop_" #i, \ + }}, + FOR_CORES +#undef FOR_CORE +}); + +static std::array<PlotHeatmap, 0> g_arch_plots_heatmaps = {}; diff --git a/arch/v1/arch.c b/arch/v1/arch.c new file mode 100644 index 0000000..ea6ff36 --- /dev/null +++ b/arch/v1/arch.c @@ -0,0 +1,989 @@ +// file : arch/v1/arch.c +// project : Salis-VM +// author : Paul Oliver <contact@pauloliver.dev> +// +// Based on the original VM architecture from Salis-V1. +// The only addition are the extra core-fields used for data aggregation. + +// index: +// [section] includes +// [section] getters +// [section] statics +// [section] callbacks +// [section] validation +// [section] data +// [section] main + +// ---------------------------------------------------------------------------- +// [section] includes +// ---------------------------------------------------------------------------- +#include <assert.h> +#include <sqlite3.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <threads.h> +#include <zlib.h> + +#include "arch.h" +#include "arch_spec.h" +#include "arch_inst.h" +#include "compress.h" +#include "logger.h" +#include "salis.h" +#include "sql.h" + +// ---------------------------------------------------------------------------- +// [section] getters +// ---------------------------------------------------------------------------- +uint64_t arch_proc_get_ip_addr(const struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + return proc_get(core, pix)->ip; +} + +uint64_t arch_proc_get_sp_addr(const struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + return proc_get(core, pix)->sp; +} + +uint64_t arch_proc_get_mb0_addr(const struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + return proc_get(core, pix)->mb0a; +} + +uint64_t arch_proc_get_mb0_size(const struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + return proc_get(core, pix)->mb0s; +} + +uint64_t arch_proc_get_mb1_addr(const struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + return proc_get(core, pix)->mb1a; +} + +uint64_t arch_proc_get_mb1_size(const struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + return proc_get(core, pix)->mb1s; +} + +uint64_t arch_proc_get_slice(const struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + (void)core; + (void)pix; + return 1; +} + +// ---------------------------------------------------------------------------- +// [section] statics +// ---------------------------------------------------------------------------- +static void increment_ip(struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + struct Proc *proc = proc_fetch(core, pix); + assert(proc->ip < MVEC_SIZE); + proc->ip++; + proc->sp = proc->ip; +} + +static bool is_between(uint8_t inst, uint8_t lo, uint8_t hi) { + assert(inst < ARCH_INST_COUNT); + assert(lo < ARCH_INST_COUNT); + assert(hi < ARCH_INST_COUNT); + assert(lo < hi); + return (inst >= lo) && (inst <= hi); +} + +static bool is_key(uint8_t inst) { + assert(inst < ARCH_INST_COUNT); + return is_between(inst, keya, keyp); +} + +#if !defined(NDEBUG) +static bool is_lock(uint8_t inst) { + assert(inst < ARCH_INST_COUNT); + return is_between(inst, loka, lokp); +} +#endif + +static bool is_rmod(uint8_t inst) { + assert(inst < ARCH_INST_COUNT); + return is_between(inst, nop0, nop3); +} + +static bool key_lock_match(uint8_t key, uint8_t lock) { + assert(key < ARCH_INST_COUNT); + assert(lock < ARCH_INST_COUNT); + assert(is_key(key)); + return (key - keya) == (lock - loka); +} + +static bool seek(struct Core *core, uint64_t pix, bool fwrd) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + struct Proc *proc = proc_fetch(core, pix); + assert(proc->ip < MVEC_SIZE); + + if (proc->ip + 1 >= MVEC_SIZE) { + increment_ip(core, pix); + return false; + } + + uint8_t next = mvec_get_inst(core, proc->ip + 1); + + if (!is_key(next)) { + increment_ip(core, pix); + return false; + } + + if (proc->sp >= MVEC_SIZE) { + // HALT: process SP address is invalid + core->hlts++; + return false; + } + + uint8_t spin = mvec_get_inst(core, proc->sp); + + if (key_lock_match(next, spin)) { + return true; + } + + if (fwrd) { + proc->sp++; + } else { + proc->sp--; + } + + return false; +} + +static void jump(struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + struct Proc *proc = proc_fetch(core, pix); + assert(proc->ip < MVEC_SIZE); + +#if !defined(NDEBUG) + uint8_t next = mvec_get_inst(core, proc->ip + 1); + uint8_t spin = mvec_get_inst(core, proc->sp); + assert(is_key(next)); + assert(is_lock(spin)); + assert(key_lock_match(next, spin)); +#endif + + proc->ip = proc->sp; +} + +static void get_reg_addr_list(struct Core *core, uint64_t pix, uint64_t **rlist, int rcount, bool offset) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + assert(rlist); + assert(rcount); + assert(rcount < 4); + struct Proc *proc = proc_fetch(core, pix); + assert(proc->ip < MVEC_SIZE); + uint64_t madr = proc->ip + (offset ? 2 : 1); + + for (int i = 0; i < rcount; i++) { + rlist[i] = &proc->r0x; + } + + for (int i = 0; i < rcount; i++) { + uint64_t mnxt = madr + i; + + if (mnxt >= MVEC_SIZE) { + break; + } + + uint8_t mins = mvec_get_inst(core, mnxt); + + if (!is_rmod(mins)) { + break; + } + + switch (mins) { + case nop0: + rlist[i] = &proc->r0x; + break; + case nop1: + rlist[i] = &proc->r1x; + break; + case nop2: + rlist[i] = &proc->r2x; + break; + case nop3: + rlist[i] = &proc->r3x; + break; + } + } +} + +static void address(struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + struct Proc *proc = proc_fetch(core, pix); + assert(proc->ip < MVEC_SIZE); + uint64_t *reg; + +#if !defined(NDEBUG) + uint8_t next = mvec_get_inst(core, proc->ip + 1); + uint8_t spin = mvec_get_inst(core, proc->sp); + assert(is_key(next)); + assert(is_lock(spin)); + assert(key_lock_match(next, spin)); +#endif + + get_reg_addr_list(core, pix, ®, 1, true); + *reg = proc->sp; + increment_ip(core, pix); +} + +static void free_memory_block(struct Core *core, uint64_t addr, uint64_t size) { + assert(core); + assert(size); + + for (uint64_t i = 0; i < size; i++) { + mvec_free(core, addr + i); + } +} + +static void free_child_memory_of(struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + struct Proc *proc = proc_fetch(core, pix); + assert(proc->ip < MVEC_SIZE); + assert(proc->mb1s); + free_memory_block(core, proc->mb1a, proc->mb1s); + proc->mb1a = 0; + proc->mb1s = 0; +} + +static void alloc(struct Core *core, uint64_t pix, bool fwrd) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + struct Proc *proc = proc_fetch(core, pix); + assert(proc->ip < MVEC_SIZE); + uint64_t *regs[2]; + get_reg_addr_list(core, pix, regs, 2, false); + uint64_t bsize = *regs[0]; + + // Do nothing if block-size is zero + if (!bsize) { + increment_ip(core, pix); + return; + } + + // Do nothing if seek pointer is not adjacent to allocated memory block + if (proc->mb1s) { + uint64_t exp_addr = proc->mb1a; + + if (fwrd) { + exp_addr += proc->mb1s; + } else { + exp_addr--; + } + + if (proc->sp != exp_addr) { + increment_ip(core, pix); + return; + } + } + + // HALT: process SP address is invalid + // Discard allocated memory if any... + if (proc->sp >= MVEC_SIZE) { + if (proc->mb1s) { + free_child_memory_of(core, pix); + } + + core->hlts++; + return; + } + + // Allocation was successful, store block address on register + if (proc->mb1s == bsize) { + increment_ip(core, pix); + *regs[1] = proc->mb1a; + return; + } + + // Seek pointer collided with another allocated block. + // Discard and keep looking... + if (mvec_is_alloc(core, proc->sp)) { + if (proc->mb1s) { + free_child_memory_of(core, pix); + } + + if (fwrd) { + proc->sp++; + } else { + proc->sp--; + } + + return; + } + + // Free (non-allocated) byte found, enlarge child block 1 byte + mvec_alloc(core, proc->sp); + + if (!proc->mb1s || !fwrd) { + proc->mb1a = proc->sp; + } + + proc->mb1s++; + + // Advance seek pointer + if (fwrd) { + proc->sp++; + } else { + proc->sp--; + } +} + +static void if_not_zero(struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + struct Proc *proc = proc_fetch(core, pix); + assert(proc->ip < MVEC_SIZE); + uint64_t *reg; + get_reg_addr_list(core, pix, ®, 1, false); + uint64_t jmod = ((proc->ip + 1 < MVEC_SIZE) && is_rmod(mvec_get_inst(core, proc->ip + 1))) ? 1 : 0; + uint64_t rmod = *reg ? 1 : 2; + proc->ip += jmod + rmod; + proc->sp = proc->ip; +} + +static void mem_block_swap(struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + struct Proc *proc = proc_fetch(core, pix); + assert(proc->ip < MVEC_SIZE); + + if (proc->mb1s) { + uint64_t tmpa = proc->mb0a; + uint64_t tmps = proc->mb0s; + proc->mb0a = proc->mb1a; + proc->mb0s = proc->mb1s; + proc->mb1a = tmpa; + proc->mb1s = tmps; + + // Memory block swap events mark all addresses within both blocks + for (uint64_t i = 0; i < proc->mb0s; i++) { + core->xeva.data[proc->mb0a + i]++; + } + + for (uint64_t i = 0; i < proc->mb1s; i++) { + core->xeva.data[proc->mb1a + i]++; + } + } + + increment_ip(core, pix); +} + +static void mem_block_clear(struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + struct Proc *proc = proc_fetch(core, pix); + assert(proc->ip < MVEC_SIZE); + + if (proc->mb1s) { + free_child_memory_of(core, pix); + } + + increment_ip(core, pix); +} + +static void mem_block_split(struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + struct Proc *proc = proc_fetch(core, pix); + assert(proc->ip < MVEC_SIZE); + + if (proc->mb1s) { + struct Proc child = { 0 }; + child.ip = proc->mb1a; + child.sp = proc->mb1a; + child.mb0a = proc->mb1a; + child.mb0s = proc->mb1s; + proc->mb1a = 0; + proc->mb1s = 0; + + // A new organism is born :) + proc_new(core, &child); + } else { + assert(!proc->mb1a); + } + + increment_ip(core, pix); +} + +static void three_regs_op(struct Core *core, uint64_t pix, uint8_t inst) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + uint64_t *regs[3]; + get_reg_addr_list(core, pix, regs, 3, false); + + // Organisms can do arithmetic using any sequence of 3 registers + switch (inst) { + case addn: + *regs[0] = *regs[1] + *regs[2]; + break; + case subn: + *regs[0] = *regs[1] - *regs[2]; + break; + case muln: + *regs[0] = *regs[1] * *regs[2]; + break; + case divn: + if (*regs[2]) { + *regs[0] = *regs[1] / *regs[2]; + } else { + // HALT: division by zero + core->hlts++; + return; + } + + break; + default: + assert(false); + } + + increment_ip(core, pix); +} + +static void one_reg_op(struct Core *core, uint64_t pix, uint8_t inst) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + uint64_t *reg; + get_reg_addr_list(core, pix, ®, 1, false); + + switch (inst) { + case incn: + (*reg)++; + break; + case decn: + (*reg)--; + break; + case notn: + *reg = !(*reg); + break; + case shfl: + *reg <<= 1; + break; + case shfr: + *reg >>= 1; + break; + case zero: + *reg = 0; + break; + case unit: + *reg = 1; + break; + default: + assert(false); + } + + increment_ip(core, pix); +} + +static int get_sp_dir(uint64_t src, uint64_t dst) { + if (src == dst) { + return 0; + } else if (src - dst <= dst - src) { + return -1; + } else { + return 1; + } +} + +static void load_reg(struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + struct Proc *proc = proc_fetch(core, pix); + assert(proc->ip < MVEC_SIZE); + uint64_t *regs[2]; + get_reg_addr_list(core, pix, regs, 2, false); + int sp_dir = get_sp_dir(proc->sp, *regs[0]); + + if (*regs[0] >= MVEC_SIZE) { + // Target address is invalid + increment_ip(core, pix); + } else if (sp_dir == 1) { + proc->sp++; + } else if (sp_dir == -1) { + proc->sp--; + } else { + *regs[1] = mvec_get_inst(core, *regs[0]); + increment_ip(core, pix); + } +} + +static bool is_writeable_by(const struct Core *core, uint64_t addr, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + return addr < MVEC_SIZE && (!mvec_is_alloc(core, addr) || mvec_is_proc_owner(core, addr, pix)); +} + +static void write_reg(struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + struct Proc *proc = proc_fetch(core, pix); + assert(proc->ip < MVEC_SIZE); + uint64_t *regs[2]; + get_reg_addr_list(core, pix, regs, 2, false); + int sp_dir = get_sp_dir(proc->sp, *regs[0]); + + if (sp_dir == 1) { + proc->sp++; + } else if (sp_dir == -1) { + proc->sp--; + } else { + if (is_writeable_by(core, *regs[0], pix)) { + uint64_t addr = *regs[0]; + uint8_t inst = *regs[1] & ARCH_INST_MASK; + + // Store write event + core->iwrt[inst]++; + core->weva.data[addr]++; + + if (mvec_is_in_mb0_of_proc(core, addr, pix)) { + core->wmb0++; + } else if (mvec_is_in_mb1_of_proc(core, addr, pix)) { + core->wmb1++; + } else { + core->wdea++; + } + + // Write instruction + mvec_set_inst(core, addr, inst); + } + + increment_ip(core, pix); + } +} + +static void two_reg_op(struct Core *core, uint64_t pix, uint8_t inst) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + uint64_t *regs[2]; + get_reg_addr_list(core, pix, regs, 2, false); + + switch (inst) { + case rdup: + *regs[1] = *regs[0]; + break; + case rswp: + { + uint64_t tmp = *regs[0]; + *regs[0] = *regs[1]; + *regs[1] = tmp; + } + + break; + default: + assert(false); + } + + increment_ip(core, pix); +} + +static void push_to_stack(struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + struct Proc *proc = proc_fetch(core, pix); + assert(proc->ip < MVEC_SIZE); + uint64_t *reg; + get_reg_addr_list(core, pix, ®, 1, false); + proc->s7 = proc->s6; + proc->s6 = proc->s5; + proc->s5 = proc->s4; + proc->s4 = proc->s3; + proc->s3 = proc->s2; + proc->s2 = proc->s1; + proc->s1 = proc->s0; + proc->s0 = *reg; + increment_ip(core, pix); +} + +static void pop_from_stack(struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + struct Proc *proc = proc_fetch(core, pix); + assert(proc->ip < MVEC_SIZE); + uint64_t *reg; + get_reg_addr_list(core, pix, ®, 1, false); + *reg = proc->s0; + proc->s0 = proc->s1; + proc->s1 = proc->s2; + proc->s2 = proc->s3; + proc->s3 = proc->s4; + proc->s4 = proc->s5; + proc->s5 = proc->s6; + proc->s6 = proc->s7; + proc->s7 = 0; + increment_ip(core, pix); +} + +// ---------------------------------------------------------------------------- +// [section] callbacks +// ---------------------------------------------------------------------------- +void arch_on_proc_step(struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + struct Proc *proc = proc_fetch(core, pix); + + if (proc->ip >= MVEC_SIZE) { + // HALT: process IP address is invalid + core->hlts++; + return; + } + + // Fetch instruction + uint8_t inst = mvec_get_inst(core, proc->ip); + core->iexe[inst]++; + + // Execute instruction + switch (inst) { + case jmpb: + if (seek(core, pix, false)) { + jump(core, pix); + } + + break; + case jmpf: + if (seek(core, pix, true)) { + jump(core, pix); + } + + break; + case adrb: + if (seek(core, pix, false)) { + address(core, pix); + } + + break; + case adrf: + if (seek(core, pix, true)) { + address(core, pix); + } + + break; + case malb: + alloc(core, pix, false); + break; + case malf: + alloc(core, pix, true); + break; + case ifnz: + if_not_zero(core, pix); + break; + case bswp: + mem_block_swap(core, pix); + break; + case bclr: + mem_block_clear(core, pix); + break; + case bspl: + mem_block_split(core, pix); + break; + case addn: + case subn: + case muln: + case divn: + three_regs_op(core, pix, inst); + break; + case incn: + case decn: + case notn: + case shfl: + case shfr: + case zero: + case unit: + one_reg_op(core, pix, inst); + break; + case rloa: + load_reg(core, pix); + break; + case rwrt: + write_reg(core, pix); + break; + case rdup: + case rswp: + two_reg_op(core, pix, inst); + break; + case rpsh: + push_to_stack(core, pix); + break; + case rpop: + pop_from_stack(core, pix); + break; + default: + increment_ip(core, pix); + break; + } +} + +void arch_on_proc_kill(struct Core *core) { + assert(core); + assert(core->pnum > 1); + struct Proc *pfst = proc_fetch(core, core->pfst); + free_memory_block(core, pfst->mb0a, pfst->mb0s); + + if (pfst->mb1s) { + free_memory_block(core, pfst->mb1a, pfst->mb1s); + } + + memcpy(pfst, &g_null_proc, sizeof(struct Proc)); +} + +// ---------------------------------------------------------------------------- +// [section] validation +// ---------------------------------------------------------------------------- +#if !defined(NDEBUG) +void arch_validate_core(struct Core *core) { + assert(core); + + for (uint64_t pix = core->pfst; pix <= core->plst; pix++) { + const struct Proc *proc = proc_get(core, pix); + assert(proc->mb0s); + + if (proc->mb1a) { + assert(proc->mb1s); + } + + for (uint64_t i = 0; i < proc->mb0s; i++) { + uint64_t addr = proc->mb0a + i; + assert(mvec_is_alloc(core, addr)); + assert(mvec_is_proc_owner(core, addr, pix)); + } + + for (uint64_t i = 0; i < proc->mb1s; i++) { + uint64_t addr = proc->mb1a + i; + assert(mvec_is_alloc(core, addr)); + assert(mvec_is_proc_owner(core, addr, pix)); + } + } +} +#endif + +// ---------------------------------------------------------------------------- +// [section] data +// ---------------------------------------------------------------------------- +void arch_push_data_header(void) { + assert(g_sim_db); + log_info("Creating arch table in SQLite database"); + + // Empty blob columns help the data-server stay agnostic of DB schema. + // These represent EVA data that will be appended by data server. + sql_exec( + NULL, NULL, + "create table arch (" +#define ARCH_SPEC_INST(label, symbol, core) \ + #label "_pop_" #core " int not null, " \ + #label "_exe_" #core " int not null, " \ + #label "_wrt_" #core " int not null, " +#define FOR_CORE(i) \ + ARCH_SPEC_INST_SET(i) \ + "wmb0_" #i " int not null, " \ + "wmb1_" #i " int not null, " \ + "wdea_" #i " int not null, " \ + "hlts_" #i " int not null, " \ + "wev" SALIS_EVENT_ARRAY_SIZE_COL_MARKER #i " int not null, wev_" #i " blob, " \ + "xev" SALIS_EVENT_ARRAY_SIZE_COL_MARKER #i " int not null, xev_" #i " blob, " + FOR_CORES +#undef FOR_CORE +#undef ARCH_SPEC_INST + "step int not null" + ");" + ); +} + +static int arch_count_inst_pop(struct Core *core) { + assert(core); + + for (uint64_t i = 0; i < MVEC_SIZE; i++) { + core->ipop[mvec_get_inst(core, i)]++; + } + +#if !defined(NDEBUG) + uint64_t pop_total = 0; + + for (uint64_t i = 0; i < ARCH_INST_COUNT; i++) { + pop_total += core->ipop[i]; + } + + assert(pop_total == MVEC_SIZE); +#endif + + return 0; +} + +void arch_prepare_data_line(void) { + assert(g_sim_db); + + // Measure instruction population and compress event-arrays in parallel + for (int i = 0; i < CORES; i++) { + struct Core *core = &g_cores[i]; + thrd_create(&core->ipop_thrd, (thrd_start_t)arch_count_inst_pop, core); + thrd_create(&core->weva.thrd, (thrd_start_t)comp_deflate, &core->weva.params); + thrd_create(&core->xeva.thrd, (thrd_start_t)comp_deflate, &core->xeva.params); + } + + for (int i = 0; i < CORES; i++) { + struct Core *core = &g_cores[i]; + thrd_join(core->ipop_thrd, NULL); + thrd_join(core->weva.thrd, NULL); + thrd_join(core->xeva.thrd, NULL); + core->weva.blob_size = core->weva.params.strm.total_out; + core->xeva.blob_size = core->xeva.params.strm.total_out; + } +} + +void arch_push_data_line(FILE *eva_file) { + assert(g_sim_db); + assert(eva_file); + assert(g_step % DATA_PUSH_INTERVAL == 0); + log_info("Pushing row to arch table in SQLite database"); + sql_exec( + NULL, NULL, + "insert into arch (" +#define ARCH_SPEC_INST(label, symbol, core) \ + #label "_pop_" #core ", " \ + #label "_exe_" #core ", " \ + #label "_wrt_" #core ", " +#define FOR_CORE(i) \ + ARCH_SPEC_INST_SET(i) \ + "wmb0_" #i ", " \ + "wmb1_" #i ", " \ + "wdea_" #i ", " \ + "hlts_" #i ", " \ + "wev" SALIS_EVENT_ARRAY_SIZE_COL_MARKER #i ", " \ + "xev" SALIS_EVENT_ARRAY_SIZE_COL_MARKER #i ", " + FOR_CORES +#undef FOR_CORE +#undef ARCH_SPEC_INST + "step" + ") values (" +#define ARCH_SPEC_INST(label, symbol, core) \ + "%ld, " \ + "%ld, " \ + "%ld, " +#define FOR_CORE(i) \ + ARCH_SPEC_INST_SET(i) \ + "%ld, " \ + "%ld, " \ + "%ld, " \ + "%ld, " \ + "%ld, " \ + "%ld, " + FOR_CORES +#undef FOR_CORE +#undef ARCH_SPEC_INST + "%ld" + ");", +#define ARCH_SPEC_INST(label, symbol, core) \ + g_cores[core].ipop[label], \ + g_cores[core].iexe[label], \ + g_cores[core].iwrt[label], +#define FOR_CORE(i) \ + ARCH_SPEC_INST_SET(i) \ + g_cores[i].wmb0, \ + g_cores[i].wmb1, \ + g_cores[i].wdea, \ + g_cores[i].hlts, \ + g_cores[i].weva.blob_size, \ + g_cores[i].xeva.blob_size, + FOR_CORES +#undef FOR_CORE +#undef ARCH_SPEC_INST + g_step + ); + + for (int i = 0; i < CORES; i++) { + struct Core *core = &g_cores[i]; + fwrite(core->weva.comp, sizeof(Bytef), core->weva.blob_size, eva_file); + fwrite(core->xeva.comp, sizeof(Bytef), core->xeva.blob_size, eva_file); + comp_deflate_end(&core->weva.params); + comp_deflate_end(&core->xeva.params); + } + + // Reset data aggregation fields + for (int i = 0; i < CORES; i++) { + struct Core *core = &g_cores[i]; + memset(core->ipop, 0, sizeof(uint64_t) * ARCH_INST_COUNT); + memset(core->iexe, 0, sizeof(uint64_t) * ARCH_INST_COUNT); + memset(core->iwrt, 0, sizeof(uint64_t) * ARCH_INST_COUNT); + core->wmb0 = 0; + core->wmb1 = 0; + core->wdea = 0; + core->hlts = 0; + memset(core->weva.data, 0, sizeof(uint64_t) * MVEC_SIZE); + memset(core->xeva.data, 0, sizeof(uint64_t) * MVEC_SIZE); + } +} + +// ---------------------------------------------------------------------------- +// [section] main +// ---------------------------------------------------------------------------- +#if defined(COMMAND_NEW) +void arch_core_init(struct Core *core) { + assert(core); + + for (uint64_t i = 0; i < CLONES; i++) { + uint64_t addr_clone = (MVEC_SIZE / CLONES) * i; + struct Proc *panc = proc_fetch(core, i); + panc->mb0a = addr_clone; + panc->mb0s = ANC_SIZE; + panc->ip = addr_clone; + panc->sp = addr_clone; + } + + core_event_array_init(&core->weva); + core_event_array_init(&core->xeva); +} +#endif + +#if defined(COMMAND_LOAD) +void arch_core_load(struct Core *core, FILE *f) { + assert(core); + assert(f); + + fread(core->ipop, sizeof(uint64_t), ARCH_INST_COUNT, f); + fread(core->iexe, sizeof(uint64_t), ARCH_INST_COUNT, f); + fread(core->iwrt, sizeof(uint64_t), ARCH_INST_COUNT, f); + fread(&core->wmb0, sizeof(uint64_t), 1, f); + fread(&core->wmb1, sizeof(uint64_t), 1, f); + fread(&core->wdea, sizeof(uint64_t), 1, f); + fread(core->weva.data, sizeof(uint64_t), MVEC_SIZE, f); + fread(core->xeva.data, sizeof(uint64_t), MVEC_SIZE, f); + + core_event_array_init(&core->weva); + core_event_array_init(&core->xeva); +} +#endif + +void arch_core_save(const struct Core *core, FILE *f) { + assert(core); + assert(f); + + fwrite(core->ipop, sizeof(uint64_t), ARCH_INST_COUNT, f); + fwrite(core->iexe, sizeof(uint64_t), ARCH_INST_COUNT, f); + fwrite(core->iwrt, sizeof(uint64_t), ARCH_INST_COUNT, f); + fwrite(&core->wmb0, sizeof(uint64_t), 1, f); + fwrite(&core->wmb1, sizeof(uint64_t), 1, f); + fwrite(&core->wdea, sizeof(uint64_t), 1, f); + fwrite(core->weva.data, sizeof(uint64_t), MVEC_SIZE, f); + fwrite(core->xeva.data, sizeof(uint64_t), MVEC_SIZE, f); +} + +void arch_core_free(struct Core *core) { + assert(core); + (void)core; +} diff --git a/arch/v1/arch_inst.c b/arch/v1/arch_inst.c new file mode 100644 index 0000000..4d75ede --- /dev/null +++ b/arch/v1/arch_inst.c @@ -0,0 +1,40 @@ +// file : arch/v1/arch_inst.c +// project : Salis-VM +// author : Paul Oliver <contact@pauloliver.dev> + +#include <assert.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#define ARCH_EXPORT_INST_COUNT +#include "arch_spec.h" +#include "arch_inst.h" + +wchar_t arch_inst_symbol(uint8_t inst) { + assert(inst < ARCH_INST_COUNT); + assert(inst_enum_count == ARCH_INST_COUNT); + + switch (inst) { +#define ARCH_SPEC_INST(label, symbol, core) case label: return symbol; + ARCH_SPEC_INST_SET(core) +#undef ARCH_SPEC_INST + } + + assert(false); + return L'\0'; +} + +const char *arch_inst_mnemonic(uint8_t inst) { + assert(inst < ARCH_INST_COUNT); + assert(inst_enum_count == ARCH_INST_COUNT); + + switch (inst) { +#define ARCH_SPEC_INST(label, symbol, core) case label: return #label; + ARCH_SPEC_INST_SET(core) +#undef ARCH_SPEC_INST + } + + assert(false); + return NULL; +} diff --git a/arch/v1/arch_spec.h b/arch/v1/arch_spec.h new file mode 100644 index 0000000..a904206 --- /dev/null +++ b/arch/v1/arch_spec.h @@ -0,0 +1,140 @@ +// file : arch/v1/arch_spec.h +// project : Salis-VM +// author : Paul Oliver <contact@pauloliver.dev> + +#pragma once + +// index: +// [section] arch-specific core fields +// [section] arch-specific process fields +// [section] instruction set +// [section] instruction enum + +// V1 has 64 instructions - 1 unused bit +#define ARCH_SPEC_INST_BITS 6 + +// ---------------------------------------------------------------------------- +// [section] arch-specific core fields +// ---------------------------------------------------------------------------- +#define ARCH_SPEC_CORE_FIELDS \ + ARCH_SPEC_CORE_FIELD(thrd_t, ipop_thrd) \ + ARCH_SPEC_CORE_FIELD(uint64_t, ipop[ARCH_INST_COUNT]) \ + ARCH_SPEC_CORE_FIELD(uint64_t, iexe[ARCH_INST_COUNT]) \ + ARCH_SPEC_CORE_FIELD(uint64_t, iwrt[ARCH_INST_COUNT]) \ + ARCH_SPEC_CORE_FIELD(uint64_t, wmb0) \ + ARCH_SPEC_CORE_FIELD(uint64_t, wmb1) \ + ARCH_SPEC_CORE_FIELD(uint64_t, wdea) \ + ARCH_SPEC_CORE_FIELD(uint64_t, hlts) \ + ARCH_SPEC_CORE_FIELD(struct EventArray, weva) \ + ARCH_SPEC_CORE_FIELD(struct EventArray, xeva) + +// ---------------------------------------------------------------------------- +// [section] arch-specific process fields +// ---------------------------------------------------------------------------- +#define ARCH_SPEC_PROC_FIELDS \ + ARCH_SPEC_PROC_FIELD(uint64_t, ip) \ + ARCH_SPEC_PROC_FIELD(uint64_t, sp) \ + ARCH_SPEC_PROC_FIELD(uint64_t, mb0a) \ + ARCH_SPEC_PROC_FIELD(uint64_t, mb0s) \ + ARCH_SPEC_PROC_FIELD(uint64_t, mb1a) \ + ARCH_SPEC_PROC_FIELD(uint64_t, mb1s) \ + ARCH_SPEC_PROC_FIELD(uint64_t, r0x) \ + ARCH_SPEC_PROC_FIELD(uint64_t, r1x) \ + ARCH_SPEC_PROC_FIELD(uint64_t, r2x) \ + ARCH_SPEC_PROC_FIELD(uint64_t, r3x) \ + ARCH_SPEC_PROC_FIELD(uint64_t, s0) \ + ARCH_SPEC_PROC_FIELD(uint64_t, s1) \ + ARCH_SPEC_PROC_FIELD(uint64_t, s2) \ + ARCH_SPEC_PROC_FIELD(uint64_t, s3) \ + ARCH_SPEC_PROC_FIELD(uint64_t, s4) \ + ARCH_SPEC_PROC_FIELD(uint64_t, s5) \ + ARCH_SPEC_PROC_FIELD(uint64_t, s6) \ + ARCH_SPEC_PROC_FIELD(uint64_t, s7) + +// ---------------------------------------------------------------------------- +// [section] instruction set +// ---------------------------------------------------------------------------- +// Extra core argument allows calling macro recursively +// within calls to FOR_CORES. See arch_push_data_header in arch/v1/arch.c +// for an example of what I mean. +#define ARCH_SPEC_INST_SET(core) \ + ARCH_SPEC_INST(noop, L'⢀', core) \ + ARCH_SPEC_INST(nop0, L'0', core) \ + ARCH_SPEC_INST(nop1, L'1', core) \ + ARCH_SPEC_INST(nop2, L'2', core) \ + ARCH_SPEC_INST(nop3, L'3', core) \ + \ + ARCH_SPEC_INST(jmpb, L'❨', core) \ + ARCH_SPEC_INST(jmpf, L'❩', core) \ + ARCH_SPEC_INST(adrb, L'⟦', core) \ + ARCH_SPEC_INST(adrf, L'⟧', core) \ + ARCH_SPEC_INST(malb, L'❴', core) \ + ARCH_SPEC_INST(malf, L'❵', core) \ + \ + ARCH_SPEC_INST(ifnz, L'?', core) \ + ARCH_SPEC_INST(bswp, L'%', core) \ + ARCH_SPEC_INST(bclr, L'∅', core) \ + ARCH_SPEC_INST(bspl, L'$', core) \ + \ + ARCH_SPEC_INST(addn, L'+', core) \ + ARCH_SPEC_INST(subn, L'−', core) \ + ARCH_SPEC_INST(muln, L'×', core) \ + ARCH_SPEC_INST(divn, L'/', core) \ + ARCH_SPEC_INST(incn, L'△', core) \ + ARCH_SPEC_INST(decn, L'▽', core) \ + ARCH_SPEC_INST(notn, L'¬', core) \ + ARCH_SPEC_INST(shfl, L'«', core) \ + ARCH_SPEC_INST(shfr, L'»', core) \ + ARCH_SPEC_INST(zero, L'z', core) \ + ARCH_SPEC_INST(unit, L'u', core) \ + \ + ARCH_SPEC_INST(rloa, L'↓', core) \ + ARCH_SPEC_INST(rwrt, L'↑', core) \ + ARCH_SPEC_INST(rdup, L':', core) \ + ARCH_SPEC_INST(rswp, L'↔', core) \ + ARCH_SPEC_INST(rpsh, L'↥', core) \ + ARCH_SPEC_INST(rpop, L'↧', core) \ + \ + ARCH_SPEC_INST(keya, L'a', core) \ + ARCH_SPEC_INST(keyb, L'b', core) \ + ARCH_SPEC_INST(keyc, L'c', core) \ + ARCH_SPEC_INST(keyd, L'd', core) \ + ARCH_SPEC_INST(keye, L'e', core) \ + ARCH_SPEC_INST(keyf, L'f', core) \ + ARCH_SPEC_INST(keyg, L'g', core) \ + ARCH_SPEC_INST(keyh, L'h', core) \ + ARCH_SPEC_INST(keyi, L'i', core) \ + ARCH_SPEC_INST(keyj, L'j', core) \ + ARCH_SPEC_INST(keyk, L'k', core) \ + ARCH_SPEC_INST(keyl, L'l', core) \ + ARCH_SPEC_INST(keym, L'm', core) \ + ARCH_SPEC_INST(keyn, L'n', core) \ + ARCH_SPEC_INST(keyo, L'o', core) \ + ARCH_SPEC_INST(keyp, L'p', core) \ + \ + ARCH_SPEC_INST(loka, L'A', core) \ + ARCH_SPEC_INST(lokb, L'B', core) \ + ARCH_SPEC_INST(lokc, L'C', core) \ + ARCH_SPEC_INST(lokd, L'D', core) \ + ARCH_SPEC_INST(loke, L'E', core) \ + ARCH_SPEC_INST(lokf, L'F', core) \ + ARCH_SPEC_INST(lokg, L'G', core) \ + ARCH_SPEC_INST(lokh, L'H', core) \ + ARCH_SPEC_INST(loki, L'I', core) \ + ARCH_SPEC_INST(lokj, L'J', core) \ + ARCH_SPEC_INST(lokk, L'K', core) \ + ARCH_SPEC_INST(lokl, L'L', core) \ + ARCH_SPEC_INST(lokm, L'M', core) \ + ARCH_SPEC_INST(lokn, L'N', core) \ + ARCH_SPEC_INST(loko, L'O', core) \ + ARCH_SPEC_INST(lokp, L'P', core) + +// ---------------------------------------------------------------------------- +// [section] instruction enum +// ---------------------------------------------------------------------------- +enum { +#define ARCH_SPEC_INST(label, symbol, core) label, + ARCH_SPEC_INST_SET(core) +#undef ARCH_SPEC_INST + inst_enum_count, +}; diff --git a/arch/v1/client_plots.h b/arch/v1/client_plots.h new file mode 100644 index 0000000..4b2c3af --- /dev/null +++ b/arch/v1/client_plots.h @@ -0,0 +1,83 @@ +// file : arch/v1/client_plots.h +// project : Salis-VM +// author : Paul Oliver <contact@pauloliver.dev> + +#pragma once + +// index: + +static std::array g_arch_traces = std::to_array<TraceNamed<ImS64>>({ +#define ARCH_SPEC_INST(label, symbol, core) \ + {#label "_pop_" #core, #label}, \ + {#label "_exe_" #core, #label}, \ + {#label "_wrt_" #core, #label}, +#define FOR_CORE(i) \ + {"wmb0_" #i, "wmb0_" #i}, \ + {"wmb1_" #i, "wmb1_" #i}, \ + {"wdea_" #i, "wdea_" #i}, \ + {"hlts_" #i, "hlts_" #i}, \ + ARCH_SPEC_INST_SET(i) + FOR_CORES +#undef FOR_CORE +#undef ARCH_SPEC_INST +}); + +static std::array g_arch_traces_heatmaps = std::to_array<TraceHeatmap<ImS64>>({ +#define FOR_CORE(i) \ + {"wev_" #i, "wev_" #i}, \ + {"xev_" #i, "xev_" #i}, + FOR_CORES +#undef FOR_CORE +}); + +static std::array g_arch_plots = std::to_array<PlotLines>({ + {"wevs", "general", { +#define FOR_CORE(i) "wmb0_" #i, "wmb1_" #i, "wdea_" #i, + FOR_CORES +#undef FOR_CORE + }}, + + {"hlts", "general", { +#define FOR_CORE(i) "hlts_" #i, + FOR_CORES +#undef FOR_CORE + }} +}); + +static std::array g_arch_plots_stacked = std::to_array<PlotStacked>({ +#define ARCH_SPEC_INST(label, symbol, core) \ + #label "_pop_" #core, +#define FOR_CORE(i) \ + {"ipop%_" #i, "population", { ARCH_SPEC_INST_SET(i) }}, + FOR_CORES +#undef FOR_CORE +#undef ARCH_SPEC_INST + +#define ARCH_SPEC_INST(label, symbol, core) \ + #label "_exe_" #core, +#define FOR_CORE(i) \ + {"iexe%_" #i, "population", { ARCH_SPEC_INST_SET(i) }}, + FOR_CORES +#undef FOR_CORE +#undef ARCH_SPEC_INST + +#define ARCH_SPEC_INST(label, symbol, core) \ + #label "_wrt_" #core, +#define FOR_CORE(i) \ + {"iwrt%_" #i, "population", { ARCH_SPEC_INST_SET(i) }}, + FOR_CORES +#undef FOR_CORE +#undef ARCH_SPEC_INST +}); + +static std::array g_arch_plots_heatmaps = std::to_array<PlotHeatmap>({ +#define FOR_CORE(i) \ + {"wev_" #i, "heatmaps", "wev_" #i}, + FOR_CORES +#undef FOR_CORE + +#define FOR_CORE(i) \ + {"xev_" #i, "heatmaps", "xev_" #i}, + FOR_CORES +#undef FOR_CORE +}); |
