From 9d08b9e61d48803d9c54a6921d573f974441ac42 Mon Sep 17 00:00:00 2001 From: Paul Oliver Date: Tue, 14 Apr 2026 23:31:04 +0200 Subject: Reorganizes C source files --- core.c | 1222 ------------------------------------------------------- core/compress.c | 56 +++ core/render.c | 86 ++++ core/salis.c | 1222 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ data/compress.c | 56 --- data/render.c | 86 ---- salis.py | 6 +- 7 files changed, 1367 insertions(+), 1367 deletions(-) delete mode 100644 core.c create mode 100644 core/compress.c create mode 100644 core/render.c create mode 100644 core/salis.c delete mode 100644 data/compress.c delete mode 100644 data/render.c diff --git a/core.c b/core.c deleted file mode 100644 index b6b5609..0000000 --- a/core.c +++ /dev/null @@ -1,1222 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#define DATA_PUSH_BUSY_TIMEOUT 600000 -#define INST_CAP 0x80 -#define INST_MASK 0x7f -#define IPC_FLAG 0x80 -#define MALL_FLAG 0x80 -#define UINT64_HALF 0x8000000000000000ul - -#if defined(COMPRESS) || defined(DATA_PUSH_PATH) -#include "data/compress.c" -#endif - -struct Proc { -#define PROC_FIELD(type, name) type name; - PROC_FIELDS -#undef PROC_FIELD -}; - -struct Core { - uint64_t cycl; - uint64_t mall; - uint64_t muta[4]; - - uint64_t pnum; - uint64_t pcap; - uint64_t pfst; - uint64_t plst; - uint64_t pcur; - uint64_t psli; - - thrd_t thrd; - uint64_t thrd_idx; - - uint64_t ivpt; - uint64_t *ivav; - uint8_t *iviv; - -#if defined(DATA_PUSH_PATH) - uint64_t emb0; // executions within mb0 counter - uint64_t emb1; // executions within mb1 counter - uint64_t eliv; // executions within not-owned live code counter (parasites) - uint64_t edea; // executions within dead code counter - - uint64_t aeva[MVEC_SIZE]; // allocation events array - uint64_t eeva[MVEC_SIZE]; // execution events array - -#define CORE_DATA_FIELD(type, name, suff) type name suff; - CORE_DATA_FIELDS -#undef CORE_FIELD -#endif - -#define CORE_FIELD(type, name, suff) type name suff; - CORE_FIELDS -#undef CORE_FIELD - - struct Proc *pvec; - uint8_t mvec[MVEC_SIZE]; - uint8_t tgap[THREAD_GAP]; -}; - -// Globals -struct Core g_cores[CORES]; -uint64_t g_steps; -uint64_t g_syncs; -const struct Proc g_dead_proc; - -#if defined(COMMAND_LOAD) || defined(COMMAND_NEW) -char g_asav_pbuf[AUTOSAVE_NAME_LEN]; -#endif - -#if defined(DATA_PUSH_PATH) -sqlite3 *g_sim_data; -#endif - -// Each UI must install these logger functions before salis_init() gets invoked -void (*g_info)(const char *fmt, ...); -void (*g_warn)(const char *fmt, ...); - -// Each architecture must define these functions -#if defined(COMMAND_BENCH) || defined(COMMAND_NEW) -void arch_core_init(struct Core *core); -#endif - -void arch_core_free(struct Core *core); - -#if defined(COMMAND_LOAD) || defined(COMMAND_NEW) -void arch_core_save(FILE *f, const struct Core *core); -#endif - -#if defined(COMMAND_LOAD) -void arch_core_load(FILE *f, struct Core *core); -#endif - -uint64_t arch_proc_mb0_addr(const struct Core *core, uint64_t pix); -uint64_t arch_proc_mb0_size(const struct Core *core, uint64_t pix); -uint64_t arch_proc_mb1_addr(const struct Core *core, uint64_t pix); -uint64_t arch_proc_mb1_size(const struct Core *core, uint64_t pix); -uint64_t arch_proc_ip_addr(const struct Core *core, uint64_t pix); -uint64_t arch_proc_sp_addr(const struct Core *core, uint64_t pix); -uint64_t arch_proc_slice(const struct Core *core, uint64_t pix); -void arch_on_proc_kill(struct Core *core); -void arch_proc_step(struct Core *core, uint64_t pix); - -#if !defined(NDEBUG) -void arch_validate_proc(const struct Core *core, uint64_t pix); -#endif - -wchar_t arch_symbol(uint8_t inst); -const char *arch_mnemonic(uint8_t inst); - -#if defined(DATA_PUSH_PATH) -#if defined(COMMAND_NEW) -void arch_push_data_header(); -#endif -void arch_push_data_line(); -#endif - -// ---------------------------------------------------------------------------- -// Memory vector functions -// ---------------------------------------------------------------------------- -#if defined(MVEC_LOOP) -uint64_t mvec_loop(uint64_t addr) { - return addr % MVEC_SIZE; -} -#endif - -bool mvec_is_alloc(const struct Core *core, uint64_t addr) { - assert(core); - -#if defined(MVEC_LOOP) - return core->mvec[mvec_loop(addr)] & MALL_FLAG ? true : false; -#else - if (addr < MVEC_SIZE) { - return core->mvec[addr] & MALL_FLAG ? true : false; - } else { - return true; - } -#endif -} - -void mvec_alloc(struct Core *core, uint64_t addr) { - assert(core); - assert(!mvec_is_alloc(core, addr)); - -#if defined(MVEC_LOOP) - core->mvec[mvec_loop(addr)] |= MALL_FLAG; -#if defined(DATA_PUSH_PATH) - // Record deallocation event - ++core->aeva[mvec_loop(addr)]; -#endif -#else - assert(addr < MVEC_SIZE); - core->mvec[addr] |= MALL_FLAG; -#if defined(DATA_PUSH_PATH) - // Record deallocation event - ++core->aeva[addr]; -#endif -#endif - core->mall++; -} - -void mvec_free(struct Core *core, uint64_t addr) { - assert(core); - assert(mvec_is_alloc(core, addr)); - -#if defined(MVEC_LOOP) - core->mvec[mvec_loop(addr)] ^= MALL_FLAG; -#if defined(DATA_PUSH_PATH) - // Record deallocation event - ++core->aeva[mvec_loop(addr)]; -#endif -#else - assert(addr < MVEC_SIZE); - core->mvec[addr] ^= MALL_FLAG; -#if defined(DATA_PUSH_PATH) - // Record deallocation event - ++core->aeva[addr]; -#endif -#endif - core->mall--; -} - -uint8_t mvec_get_byte(const struct Core *core, uint64_t addr) { - assert(core); - -#if defined(MVEC_LOOP) - return core->mvec[mvec_loop(addr)]; -#else - if (addr < MVEC_SIZE) { - return core->mvec[addr]; - } else { - return 0; - } -#endif -} - -uint8_t mvec_get_inst(const struct Core *core, uint64_t addr) { - assert(core); - -#if defined(MVEC_LOOP) - return core->mvec[mvec_loop(addr)] & INST_MASK; -#else - if (addr < MVEC_SIZE) { - return core->mvec[addr] & INST_MASK; - } else { - return 0; - } -#endif -} - -void mvec_set_inst(struct Core *core, uint64_t addr, uint8_t inst) { - assert(core); - assert(inst < INST_CAP); - -#if defined(MVEC_LOOP) - core->mvec[mvec_loop(addr)] &= MALL_FLAG; - core->mvec[mvec_loop(addr)] |= inst; -#else - assert(addr < MVEC_SIZE); - core->mvec[addr] &= MALL_FLAG; - core->mvec[addr] |= inst; -#endif -} - -#if defined(MUTA_FLIP) -void mvec_flip_bit(struct Core *core, uint64_t addr, int bit) { - assert(core); - assert(bit < 8); - core->mvec[addr] ^= (1 << bit) & INST_MASK; -} -#endif - -bool mvec_proc_is_live(const struct Core *core, uint64_t pix) { - assert(core); - - return pix >= core->pfst && pix <= core->plst; -} - -bool mvec_is_in_mb0_of_proc(const struct Core *core, uint64_t addr, uint64_t pix) { - assert(core); - assert(mvec_proc_is_live(core, pix)); - - uint64_t mb0a = arch_proc_mb0_addr(core, pix); - uint64_t mb0s = arch_proc_mb0_size(core, pix); - - return ((addr - mb0a) % MVEC_SIZE) < mb0s; -} - -bool mvec_is_in_mb1_of_proc(const struct Core *core, uint64_t addr, uint64_t pix) { - assert(core); - assert(mvec_proc_is_live(core, pix)); - - uint64_t mb1a = arch_proc_mb1_addr(core, pix); - uint64_t mb1s = arch_proc_mb1_size(core, pix); - - return ((addr - mb1a) % MVEC_SIZE) < mb1s; -} - -bool mvec_is_proc_owner(const struct Core *core, uint64_t addr, uint64_t pix) { - assert(core); - assert(mvec_proc_is_live(core, pix)); - return mvec_is_in_mb0_of_proc(core, addr, pix) || mvec_is_in_mb1_of_proc(core, addr, pix); -} - -uint64_t mvec_get_owner(const struct Core *core, uint64_t addr) { - assert(core); - assert(mvec_is_alloc(core, addr)); - - for (uint64_t pix = core->pfst; pix <= core->plst; ++pix) { - if (mvec_is_proc_owner(core, addr, pix)) { - return pix; - } - } - - assert(false); - return -1; -} - -// ---------------------------------------------------------------------------- -// Mutator functions -// ---------------------------------------------------------------------------- -#if defined(COMMAND_BENCH) || defined(COMMAND_NEW) -uint64_t muta_smix(uint64_t *seed) { - assert(seed); - - uint64_t next = (*seed += 0x9e3779b97f4a7c15); - next = (next ^ (next >> 30)) * 0xbf58476d1ce4e5b9; - next = (next ^ (next >> 27)) * 0x94d049bb133111eb; - - return next ^ (next >> 31); -} -#endif - -uint64_t muta_ro64(uint64_t x, int k) { - return (x << k) | (x >> (64 - k)); -} - -uint64_t muta_next(struct Core *core) { - assert(core); - - uint64_t r = muta_ro64(core->muta[1] * 5, 7) * 9; - uint64_t t = core->muta[1] << 17; - - core->muta[2] ^= core->muta[0]; - core->muta[3] ^= core->muta[1]; - core->muta[1] ^= core->muta[2]; - core->muta[0] ^= core->muta[3]; - - core->muta[2] ^= t; - core->muta[3] = muta_ro64(core->muta[3], 45); - - return r; -} - -void muta_cosmic_ray(struct Core *core) { - assert(core); - - uint64_t a = muta_next(core) % MUTA_RANGE; - uint64_t b = muta_next(core); - - if (a < MVEC_SIZE) { -#if defined(MUTA_FLIP) - mvec_flip_bit(core, a, (int)(b % 8)); -#else - mvec_set_inst(core, a, b & INST_MASK); -#endif - } -} - -// ---------------------------------------------------------------------------- -// Process functions -// ---------------------------------------------------------------------------- -void proc_new(struct Core *core, const struct Proc *proc) { - assert(core); - assert(proc); - - if (core->pnum == core->pcap) { - // Reallocate dynamic array - uint64_t new_pcap = core->pcap * 2; - struct Proc *new_pvec = calloc(new_pcap, sizeof(struct Proc)); - - for (uint64_t pix = core->pfst; pix <= core->plst; ++pix) { - uint64_t iold = pix % core->pcap; - uint64_t inew = pix % new_pcap; - memcpy(&new_pvec[inew], &core->pvec[iold], sizeof(struct Proc)); - } - - free(core->pvec); - core->pcap = new_pcap; - core->pvec = new_pvec; - } - - core->pnum++; - core->plst++; - memcpy(&core->pvec[core->plst % core->pcap], proc, sizeof(struct Proc)); -} - -void proc_kill(struct Core *core) { - assert(core); - assert(core->pnum > 1); - - arch_on_proc_kill(core); - - core->pcur++; - core->pfst++; - core->pnum--; -} - -const struct Proc *proc_get(const struct Core *core, uint64_t pix) { - assert(core); - - if (mvec_proc_is_live(core, pix)) { - return &core->pvec[pix % core->pcap]; - } else { - return &g_dead_proc; - } -} - -struct Proc *proc_fetch(struct Core *core, uint64_t pix) { - assert(core); - assert(mvec_proc_is_live(core, pix)); - - return &core->pvec[pix % core->pcap]; -} - -// ---------------------------------------------------------------------------- -// Core functions -// ---------------------------------------------------------------------------- -#if defined(COMMAND_LOAD) || defined(COMMAND_NEW) -void core_save(FILE *f, const struct Core *core) { - assert(f); - assert(core); - - fwrite(&core->cycl, sizeof(uint64_t), 1, f); - fwrite(&core->mall, sizeof(uint64_t), 1, f); - fwrite(core->muta, sizeof(uint64_t), 4, f); - fwrite(&core->pnum, sizeof(uint64_t), 1, f); - fwrite(&core->pcap, sizeof(uint64_t), 1, f); - fwrite(&core->pfst, sizeof(uint64_t), 1, f); - fwrite(&core->plst, sizeof(uint64_t), 1, f); - fwrite(&core->pcur, sizeof(uint64_t), 1, f); - fwrite(&core->psli, sizeof(uint64_t), 1, f); - fwrite(&core->ivpt, sizeof(uint64_t), 1, f); -#if defined(DATA_PUSH_PATH) - fwrite(&core->emb0, sizeof(uint64_t), 1, f); - fwrite(&core->emb1, sizeof(uint64_t), 1, f); - fwrite(&core->eliv, sizeof(uint64_t), 1, f); - fwrite(&core->edea, sizeof(uint64_t), 1, f); -#endif - - fwrite(core->iviv, sizeof(uint8_t), SYNC_INTERVAL, f); - fwrite(core->ivav, sizeof(uint64_t), SYNC_INTERVAL, f); - fwrite(core->pvec, sizeof(struct Proc), core->pcap, f); - fwrite(core->mvec, sizeof(uint8_t), MVEC_SIZE, f); -#if defined(DATA_PUSH_PATH) - fwrite(core->aeva, sizeof(uint64_t), MVEC_SIZE, f); - fwrite(core->eeva, sizeof(uint64_t), MVEC_SIZE, f); -#endif - - arch_core_save(f, core); -} -#endif - -#if defined(COMMAND_BENCH) || defined(COMMAND_NEW) -#if defined(ANC_BYTES) -void core_assemble_ancestor(struct Core *core) { - assert(core); - -#if defined(MVEC_LOOP) - uint64_t addr = UINT64_HALF; -#else - uint64_t addr = 0; -#endif - - uint8_t anc_bytes[] = ANC_BYTES; - - for (uint64_t i = 0; i < sizeof(anc_bytes); ++i, ++addr) { - for (uint64_t j = 0; j < CLONES; ++j) { - uint64_t addr_clone = addr + (MVEC_SIZE / CLONES) * j; - - mvec_alloc(core, addr_clone); - mvec_set_inst(core, addr_clone, anc_bytes[i]); - } - } -} -#endif - -void core_init(struct Core *core, uint64_t *seed) { - assert(core); - assert(seed); - - if (*seed) { - core->muta[0] = muta_smix(seed); - core->muta[1] = muta_smix(seed); - core->muta[2] = muta_smix(seed); - core->muta[3] = muta_smix(seed); - } - - core->pnum = CLONES; - core->pcap = CLONES; - core->plst = CLONES - 1; - core->iviv = calloc(SYNC_INTERVAL, sizeof(uint8_t)); - core->ivav = calloc(SYNC_INTERVAL, sizeof(uint64_t)); - core->pvec = calloc(core->pcap, sizeof(struct Proc)); - - assert(core->iviv); - assert(core->ivav); - assert(core->pvec); - -#if defined(ANC_BYTES) - core_assemble_ancestor(core); - arch_core_init(core); -#endif -} -#endif - -#if defined(COMMAND_LOAD) -void core_load(FILE *f, struct Core *core) { - assert(f); - assert(core); - - fread(&core->cycl, sizeof(uint64_t), 1, f); - fread(&core->mall, sizeof(uint64_t), 1, f); - fread(core->muta, sizeof(uint64_t), 4, f); - fread(&core->pnum, sizeof(uint64_t), 1, f); - fread(&core->pcap, sizeof(uint64_t), 1, f); - fread(&core->pfst, sizeof(uint64_t), 1, f); - fread(&core->plst, sizeof(uint64_t), 1, f); - fread(&core->pcur, sizeof(uint64_t), 1, f); - fread(&core->psli, sizeof(uint64_t), 1, f); - fread(&core->ivpt, sizeof(uint64_t), 1, f); -#if defined(DATA_PUSH_PATH) - fread(&core->emb0, sizeof(uint64_t), 1, f); - fread(&core->emb1, sizeof(uint64_t), 1, f); - fread(&core->eliv, sizeof(uint64_t), 1, f); - fread(&core->edea, sizeof(uint64_t), 1, f); -#endif - - core->iviv = calloc(SYNC_INTERVAL, sizeof(uint8_t)); - core->ivav = calloc(SYNC_INTERVAL, sizeof(uint64_t)); - core->pvec = calloc(core->pcap, sizeof(struct Proc)); - - assert(core->iviv); - assert(core->ivav); - assert(core->pvec); - - fread(core->iviv, sizeof(uint8_t), SYNC_INTERVAL, f); - fread(core->ivav, sizeof(uint64_t), SYNC_INTERVAL, f); - fread(core->pvec, sizeof(struct Proc), core->pcap, f); - fread(core->mvec, sizeof(uint8_t), MVEC_SIZE, f); -#if defined(DATA_PUSH_PATH) - fread(core->aeva, sizeof(uint64_t), MVEC_SIZE, f); - fread(core->eeva, sizeof(uint64_t), MVEC_SIZE, f); -#endif - - arch_core_load(f, core); -} -#endif - -void core_pull_ipcm(struct Core *core) { - assert(core); - assert(core->ivpt < SYNC_INTERVAL); - - uint8_t *iinst = &core->iviv[core->ivpt]; - uint64_t *iaddr = &core->ivav[core->ivpt]; - - if ((*iinst & IPC_FLAG) != 0) { - mvec_set_inst(core, *iaddr, *iinst & INST_MASK); - - *iinst = 0; - *iaddr = 0; - } - - assert(*iinst == 0); - assert(*iaddr == 0); -} - -void core_push_ipcm(struct Core *core, uint8_t inst, uint64_t addr) { - assert(core); - assert(core->ivpt < SYNC_INTERVAL); - assert((inst & IPC_FLAG) == 0); - - uint8_t *iinst = &core->iviv[core->ivpt]; - uint64_t *iaddr = &core->ivav[core->ivpt]; - - assert(*iinst == 0); - assert(*iaddr == 0); - - *iinst = inst | IPC_FLAG; - *iaddr = addr; -} - -void core_step(struct Core *core) { - assert(core); - - if (core->psli != 0) { - core_pull_ipcm(core); - -#if defined(DATA_PUSH_PATH) - // Save execution event locations in database - assert(mvec_proc_is_live(core, core->pcur)); - - uint64_t pcur_ip = arch_proc_ip_addr(core, core->pcur); - - if (mvec_is_in_mb0_of_proc(core, pcur_ip, core->pcur)) { - ++core->emb0; - } else if (mvec_is_in_mb1_of_proc(core, pcur_ip, core->pcur)) { - ++core->emb1; - } else if (mvec_is_alloc(core, pcur_ip)) { - ++core->eliv; - } else { - ++core->edea; - } - -#if defined(MVEC_LOOP) - core->eeva[mvec_loop(pcur_ip)]++; -#else - if (pcur_ip < MVEC_SIZE) { - core->eeva[pcur_ip]++; - } -#endif -#endif - - arch_proc_step(core, core->pcur); - - core->psli--; - core->ivpt++; - - return; - } - - if (core->pcur != core->plst) { - core->psli = arch_proc_slice(core, ++core->pcur); - core_step(core); - return; - } - - core->pcur = core->pfst; - core->psli = arch_proc_slice(core, core->pcur); - core->cycl++; - - // TODO: Implement a day-night cycle - while (core->mall > MVEC_SIZE / 2 && core->pnum > 1) { - proc_kill(core); - } - - muta_cosmic_ray(core); - core_step(core); -} - -// ---------------------------------------------------------------------------- -// Main salis functions -// ---------------------------------------------------------------------------- -#if defined(COMMAND_LOAD) || defined(COMMAND_NEW) -void salis_save(const char *path) { -#if defined(COMPRESS) - size_t size = 0; - char *in = NULL; - FILE *f = open_memstream(&in, &size); -#else - FILE *f = fopen(path, "wb"); -#endif - - assert(f); - - for (int i = 0; i < CORES; ++i) { - core_save(f, &g_cores[i]); - } - - fwrite(&g_steps, sizeof(uint64_t), 1, f); - fwrite(&g_syncs, sizeof(uint64_t), 1, f); - fclose(f); - -#if defined(COMPRESS) - assert(size); - - char *out = malloc(size); - assert(out); - - z_stream strm = { 0 }; - salis_deflate(&strm, size, (Bytef *)in, (Bytef *)out); - - FILE *fx = fopen(path, "wb"); - assert(fx); - - fwrite(&size, sizeof(size_t), 1, fx); - fwrite(out, sizeof(char), strm.total_out, fx); - fclose(fx); - - salis_deflate_end(&strm); - - free(in); - free(out); -#endif -} - -void salis_auto_save() { -#if defined(NDEBUG) - snprintf( -#else - int rem = snprintf( -#endif - g_asav_pbuf, - AUTOSAVE_NAME_LEN, - "%s-%#018lx", - SIM_PATH, - g_steps - ); - - assert(rem >= 0); - assert(rem < AUTOSAVE_NAME_LEN); - - g_info("Saving simulation state on step '%#lx'", g_steps); - salis_save(g_asav_pbuf); -} -#endif - -#if defined(DATA_PUSH_PATH) -void salis_exec_sql(int blob_cnt, const void **blobs, const int *blob_sizes, const char *sql_format, ...) { - assert(sql_format); - - va_list args; - va_start(args, sql_format); - int sql_len = vsnprintf(NULL, 0, sql_format, args) + 1; - char *sql_str = malloc(sql_len); - assert(sql_str); - va_end(args); - - va_start(args, sql_format); - vsprintf(sql_str, sql_format, args); - va_end(args); - - int sql_res; - sqlite3_stmt *sql_stmt; - - sql_res = sqlite3_prepare_v2(g_sim_data, sql_str, -1, &sql_stmt, NULL); - assert(sql_res == SQLITE_OK); - free(sql_str); - - for (int i = 0; i < blob_cnt; ++i) { - assert(blobs[i]); - sql_res = sqlite3_bind_blob(sql_stmt, i + 1, blobs[i], blob_sizes[i], SQLITE_STATIC); - assert(sql_res == SQLITE_OK); - } - - // Only handle SQLITE_BUSY error, in which case we retry the query. - // Setting 'journal_mode=wal;' should help prevent busy database errors. - while (true) { - sql_res = sqlite3_step(sql_stmt); - - if (sql_res == SQLITE_DONE || sql_res == SQLITE_ROW) { - break; - } - - g_warn("SQLite database returned error '%d' with message:", sql_res); - g_warn(sqlite3_errmsg(g_sim_data)); - - if (sql_res == SQLITE_BUSY) { - g_info("Will retry query..."); - continue; - } - - assert(false); - } - - sqlite3_finalize(sql_stmt); -} -#endif - -#if defined(DATA_PUSH_PATH) -#if defined(COMMAND_NEW) -void salis_push_data_header() { - assert(g_sim_data); - - g_info("Creating 'general' table in SQLite database"); - salis_exec_sql( - 0, NULL, NULL, - "create table general (" -#define FOR_CORE(i) \ - "cycl_" #i " int not null, " \ - "mall_" #i " int not null, " \ - "pnum_" #i " int not null, " \ - "pfst_" #i " int not null, " \ - "plst_" #i " int not null, " \ - "amb0_" #i " real not null, " \ - "amb1_" #i " real not null, " \ - "emb0_" #i " int not null, " \ - "emb1_" #i " int not null, " \ - "eliv_" #i " int not null, " \ - "edea_" #i " int not null, " - FOR_CORES -#undef FOR_CORE - "step int not null" - ");" - ); - - // Memory events - char *eprefs[] = { "aev", "eev" }; - int eprefs_cnt = sizeof(eprefs) / sizeof(eprefs[0]); - - for (int i = 0; i < CORES; ++i) { - for (int j = 0; j < eprefs_cnt; ++j) { - g_info("Creating '%s_%d' table in SQLite database", eprefs[j], i); - salis_exec_sql( - 0, NULL, NULL, - "create table %s_%d (" -#define FOR_CORE(i) "cycl_" #i " int not null, " - FOR_CORES -#undef FOR_CORE - "size int not null, " - "evts blob not null ," - "step int not null" - ");", - eprefs[j], i - ); - } - } - - arch_push_data_header(); -} -#endif - -void salis_push_data_line() { - assert(g_sim_data); - - // Measure average membory block sizes - double amb0[CORES] = { 0 }; - double amb1[CORES] = { 0 }; - - for (int i = 0; i < CORES; ++i) { - struct Core *core = &g_cores[i]; - - for (uint64_t j = core->pfst; j <= core->plst; ++j) { - amb0[i] += (double)arch_proc_mb0_size(core, j); - amb1[i] += (double)arch_proc_mb1_size(core, j); - } - - amb0[i] /= core->pnum; - amb1[i] /= core->pnum; - } - - g_info("Pushing row to 'general' table in SQLite database"); - salis_exec_sql( - 0, NULL, NULL, - "insert into general (" -#define FOR_CORE(i) \ - "cycl_" #i ", " \ - "mall_" #i ", " \ - "pnum_" #i ", " \ - "pfst_" #i ", " \ - "plst_" #i ", " \ - "amb0_" #i ", " \ - "amb1_" #i ", " \ - "emb0_" #i ", " \ - "emb1_" #i ", " \ - "eliv_" #i ", " \ - "edea_" #i ", " - FOR_CORES -#undef FOR_CORE - "step" - ") values (" -#define FOR_CORE(i) "%ld, %ld, %ld, %ld, %ld, %f, %f, %ld, %ld, %ld, %ld, " - FOR_CORES -#undef FOR_CORE - "%ld" - ");", -#define FOR_CORE(i) \ - g_cores[i].cycl, \ - g_cores[i].mall, \ - g_cores[i].pnum, \ - g_cores[i].pfst, \ - g_cores[i].plst, \ - amb0[i], \ - amb1[i], \ - g_cores[i].emb0, \ - g_cores[i].emb1, \ - g_cores[i].eliv, \ - g_cores[i].edea, - FOR_CORES -#undef FOR_CORE - g_steps - ); - - // TODO: insert execute memory events - char *eprefs[] = { "aev", "eev" }; - int eprefs_cnt = sizeof(eprefs) / sizeof(eprefs[0]); - - for (int i = 0; i < CORES; ++i) { - for (int j = 0; j < eprefs_cnt; ++j) { - uint64_t *in = NULL; - - if (!strcmp("aev", eprefs[j])) { - in = g_cores[i].aeva; - } else if (!strcmp("eev", eprefs[j])) { - in = g_cores[i].eeva; - } - - // Compress event data - size_t size = sizeof(uint64_t) * MVEC_SIZE; - char *out = malloc(size); - assert(out); - - z_stream strm = { 0 }; - salis_deflate(&strm, size, (Bytef *)in, (Bytef *)out); - - // Insert blob - const void *blob = out; - int blob_size = strm.total_out; - - g_info("Pushing row to '%s_%d' table in SQLite database", eprefs[j], i); - salis_exec_sql( - 1, &blob, &blob_size, - "insert into %s_%d (" -#define FOR_CORE(i) "cycl_" #i ", " - FOR_CORES -#undef FOR_CORE - "size, evts, step" - ") values (" -#define FOR_CORE(i) "%ld, " - FOR_CORES -#undef FOR_CORE - "%ld, ?, %ld" - ");", - eprefs[j], i, -#define FOR_CORE(i) g_cores[i].cycl, - FOR_CORES -#undef FOR_CORE - blob_size, g_steps - ); - - salis_deflate_end(&strm); - free(out); - } - } - - // Reset data aggregation fields - for (int i = 0; i < CORES; ++i) { - struct Core *core = &g_cores[i]; - - core->emb0 = 0; - core->emb1 = 0; - core->eliv = 0; - core->edea = 0; - - memset(core->aeva, 0, sizeof(uint64_t) * MVEC_SIZE); - memset(core->eeva, 0, sizeof(uint64_t) * MVEC_SIZE); - } - - // Push arch-specific data - arch_push_data_line(); -} -#endif - -#if defined(COMMAND_BENCH) || defined(COMMAND_NEW) -void salis_init() { - assert(g_info); - assert(g_warn); - - uint64_t seed = SEED; - - for (int i = 0; i < CORES; ++i) { - core_init(&g_cores[i], &seed); - } - -#if defined(COMMAND_NEW) - salis_auto_save(); -#endif - -#if defined(DATA_PUSH_PATH) - sqlite3_open(DATA_PUSH_PATH, &g_sim_data); - assert(g_sim_data); - - // Install busy handler to retry transactions if DB is locked - sqlite3_busy_timeout(g_sim_data, DATA_PUSH_BUSY_TIMEOUT); - - // Enable Write-Ahead Logging (WAL) - // This seems to help prevent DB locks when displaying live data. - // See: https://sqlite.org/wal.html - salis_exec_sql(0, NULL, NULL, "pragma journal_mode=wal;"); - - // Initialize database - salis_push_data_header(); - salis_push_data_line(); -#endif -} -#endif - -#if defined(COMMAND_LOAD) -void salis_load() { -#if defined(COMPRESS) - FILE *fx = fopen(SIM_PATH, "rb"); - assert(fx); - - fseek(fx, 0, SEEK_END); - size_t x_size = ftell(fx) - sizeof(size_t); - char *in = malloc(x_size); - rewind(fx); - assert(x_size); - assert(in); - - size_t size = 0; - fread(&size, sizeof(size_t), 1, fx); - fread(in, 1, x_size, fx); - fclose(fx); - assert(size); - - char *out = malloc(size); - assert(out); - - z_stream strm = { 0 }; - salis_inflate(&strm, x_size, size, (Bytef *)in, (Bytef *)out); - salis_inflate_end(&strm); - - FILE *f = fmemopen(out, size, "rb"); -#else - FILE *f = fopen(SIM_PATH, "rb"); -#endif - - assert(f); - - for (int i = 0; i < CORES; ++i) { - core_load(f, &g_cores[i]); - } - - fread(&g_steps, sizeof(uint64_t), 1, f); - fread(&g_syncs, sizeof(uint64_t), 1, f); - fclose(f); - -#if defined(COMPRESS) - free(in); - free(out); -#endif - -#if defined(DATA_PUSH_PATH) - sqlite3_open(DATA_PUSH_PATH, &g_sim_data); - assert(g_sim_data); - - // Install busy handler to retry transactions if DB is locked - sqlite3_busy_timeout(g_sim_data, DATA_PUSH_BUSY_TIMEOUT); -#endif -} -#endif - -int salis_thread(struct Core *core) { - assert(core); - - for (uint64_t i = 0; i < core->thrd_idx; ++i) { - core_step(core); - } - - return 0; -} - -void salis_run_thread(uint64_t ns) { - for (int i = 0; i < CORES; ++i) { - g_cores[i].thrd_idx = ns; - - thrd_create( - &g_cores[i].thrd, - (thrd_start_t)salis_thread, - &g_cores[i] - ); - } - - for (int i = 0; i < CORES; ++i) { - thrd_join(g_cores[i].thrd, NULL); - } - - g_steps += ns; -} - -void salis_sync() { - uint8_t *iviv0 = g_cores[0].iviv; - uint64_t *ivav0 = g_cores[0].ivav; - - for (int i = 1; i < CORES; ++i) { - g_cores[i - 1].iviv = g_cores[i].iviv; - g_cores[i - 1].ivav = g_cores[i].ivav; - } - - g_cores[CORES - 1].iviv = iviv0; - g_cores[CORES - 1].ivav = ivav0; - - for (int i = 0; i < CORES; ++i) { - g_cores[i].ivpt = 0; - } - - g_syncs++; -} - -void salis_loop(uint64_t ns, uint64_t dt) { - assert(dt); - - if (ns < dt) { - salis_run_thread(ns); - return; - } - - salis_run_thread(dt); - salis_sync(); - -#if defined(COMMAND_LOAD) || defined(COMMAND_NEW) - if (g_steps % AUTOSAVE_INTERVAL == 0) { - salis_auto_save(); - } -#endif - -#if defined(DATA_PUSH_PATH) - if (g_steps % DATA_PUSH_INTERVAL == 0) { - salis_push_data_line(); - } -#endif - - salis_loop(ns - dt, SYNC_INTERVAL); -} - -#if !defined(NDEBUG) -void salis_validate_core(const struct Core *core) { - assert(core->cycl <= g_steps); - assert(core->plst >= core->pfst); - assert(core->pnum == core->plst + 1 - core->pfst); - assert(core->pnum <= core->pcap); - assert(core->pcur >= core->pfst && core->pcur <= core->plst); - - uint64_t mall = 0; - - for (uint64_t i = 0; i < MVEC_SIZE; ++i) { - mall += mvec_is_alloc(core, i) ? 1 : 0; - } - - assert(core->mall == mall); - - for (uint64_t i = core->pfst; i <= core->plst; ++i) { - arch_validate_proc(core, i); - } - - for (uint64_t i = 0; i < SYNC_INTERVAL; ++i) { - uint8_t iinst = core->iviv[i]; - - if ((iinst & IPC_FLAG) == 0) { - uint64_t iaddr = core->ivav[i]; - - assert(iinst == 0); - assert(iaddr == 0); - } - } - - assert(core->ivpt == g_steps % SYNC_INTERVAL); -} - -void salis_validate() { - assert(g_steps / SYNC_INTERVAL == g_syncs); - - for (int i = 0; i < CORES; ++i) { - salis_validate_core(&g_cores[i]); - } -} -#endif - -void salis_step(uint64_t ns) { - assert(ns); - salis_loop(ns, SYNC_INTERVAL - (g_steps % SYNC_INTERVAL)); - -#if !defined(NDEBUG) - salis_validate(); -#endif -} - -void salis_free() { -#if defined(DATA_PUSH_PATH) - assert(g_sim_data); - sqlite3_close(g_sim_data); -#endif - - for (int i = 0; i < CORES; ++i) { - arch_core_free(&g_cores[i]); - - assert(g_cores[i].pvec); - assert(g_cores[i].iviv); - assert(g_cores[i].ivav); - - free(g_cores[i].pvec); - free(g_cores[i].iviv); - free(g_cores[i].ivav); - - g_cores[i].pvec = NULL; - g_cores[i].iviv = NULL; - g_cores[i].ivav = NULL; - } -} - -// ---------------------------------------------------------------------------- -// Architecture -// ---------------------------------------------------------------------------- -#include "arch.c" - -// ---------------------------------------------------------------------------- -// UI -// ---------------------------------------------------------------------------- -#if defined(COMMAND_LOAD) || defined(COMMAND_NEW) -#include "ui.c" -#endif - -// ---------------------------------------------------------------------------- -// Benchmark -// ---------------------------------------------------------------------------- -#if defined(COMMAND_BENCH) -void log_impl(const char *format, ...) { - va_list args; - va_start(args, format); - vprintf(format, args); - va_end(args); -} - -int main() { - g_info = log_impl; - g_warn = log_impl; - - g_info("Salis Benchmark Test\n\n"); - - salis_init(); - salis_step(STEPS); - - g_info("seed => %#lx\n", SEED); - g_info("g_steps => %#lx\n", g_steps); - g_info("g_syncs => %#lx\n", g_syncs); - - for (int i = 0; i < CORES; ++i) { - g_info("\n"); - g_info("core %d mall => %#lx\n", i, g_cores[i].mall); - g_info("core %d mut0 => %#lx\n", i, g_cores[i].muta[0]); - g_info("core %d mut1 => %#lx\n", i, g_cores[i].muta[1]); - g_info("core %d mut2 => %#lx\n", i, g_cores[i].muta[2]); - g_info("core %d mut3 => %#lx\n", i, g_cores[i].muta[3]); - g_info("core %d pnum => %#lx\n", i, g_cores[i].pnum); - g_info("core %d pcap => %#lx\n", i, g_cores[i].pcap); - g_info("core %d pfst => %#lx\n", i, g_cores[i].pfst); - g_info("core %d plst => %#lx\n", i, g_cores[i].plst); - g_info("core %d pcur => %#lx\n", i, g_cores[i].pcur); - g_info("core %d psli => %#lx\n", i, g_cores[i].psli); - g_info("core %d cycl => %#lx\n", i, g_cores[i].cycl); - g_info("core %d ivpt => %#lx\n", i, g_cores[i].ivpt); - g_info("\n"); - - for (int j = 0; j < 32; ++j) { - g_info("%02x ", g_cores[i].mvec[j]); - } - - g_info("\n"); - } - - salis_free(); -} -#endif diff --git a/core/compress.c b/core/compress.c new file mode 100644 index 0000000..df61123 --- /dev/null +++ b/core/compress.c @@ -0,0 +1,56 @@ +void salis_deflate(z_stream *strm, size_t size, Bytef *in, Bytef *out) { + assert(strm); + assert(size); + assert(in); + assert(out); + + strm->zalloc = NULL; + strm->zfree = NULL; + strm->opaque = NULL; + + deflateInit(strm, Z_DEFAULT_COMPRESSION); + + strm->avail_in = size; + strm->avail_out = size; + strm->next_in = in; + strm->next_out = out; + + deflate(strm, Z_FINISH); +} + +void salis_deflate_end(z_stream *strm) { + assert(strm); + + deflateEnd(strm); +} + +void salis_inflate(z_stream *strm, size_t avail_in, size_t size, Bytef *in, Bytef *out) { + assert(strm); + assert(avail_in); + assert(size); + assert(in); + assert(out); + + strm->next_in = in; + strm->avail_in = avail_in; + strm->zalloc = NULL; + strm->zfree = NULL; + strm->opaque = NULL; + + inflateInit(strm); + + strm->avail_out = size; + strm->next_out = out; + +#if defined(NDEBUG) + inflate(strm, Z_FINISH); +#else + assert(inflate(strm, Z_FINISH)); +#endif +} + +void salis_inflate_end(z_stream *strm) { + assert(strm); + + inflateEnd(strm); +} diff --git a/core/render.c b/core/render.c new file mode 100644 index 0000000..f9da65d --- /dev/null +++ b/core/render.c @@ -0,0 +1,86 @@ +#include +SQLITE_EXTENSION_INIT1 + +#include +#include +#include +#include + +#include "compress.c" + +#define EVA_SIZE (sizeof(uint64_t) * MVEC_SIZE) + +void eva_render(sqlite3_context *context, int argc, sqlite3_value **argv) { + assert(context); + assert(argc == 4); + assert(argv); + + (void)argc; + + size_t left = (size_t)sqlite3_value_int(argv[0]); +#if defined(MVEC_LOOP) + left %= MVEC_SIZE; +#endif + + size_t px_count = (size_t)sqlite3_value_int(argv[1]); + size_t px_pow = (size_t)sqlite3_value_int(argv[2]); + size_t px_res = 1 << px_pow; +#if !defined(MVEC_LOOP) +#if !defined(NDEBUG) + size_t right = left + px_res * px_count; +#endif + assert(left < MVEC_SIZE); + assert(right <= MVEC_SIZE); +#endif + + const void *blob = sqlite3_value_blob(argv[3]); + size_t blob_size = (size_t)sqlite3_value_bytes(argv[3]); + + // Inflate blob + size_t out_size = sizeof(uint64_t) * px_count; + uint64_t *eva = sqlite3_malloc(EVA_SIZE); + uint64_t *out = sqlite3_malloc(out_size); + z_stream strm = { 0 }; + salis_inflate(&strm, blob_size, EVA_SIZE, (Bytef *)blob, (Bytef *)eva); + salis_inflate_end(&strm); + + // Render image + for (size_t i = 0; i < px_count; i++) { + out[i] = 0; + + for (size_t j = 0; j < px_res; j++) { + size_t in_coord = left + i * px_res + j; +#if defined(MVEC_LOOP) + in_coord %= MVEC_SIZE; +#endif + out[i] += eva[in_coord]; + } + } + + sqlite3_free(eva); + + // Transform rendered image into textual representation + // A comma-separated list of hexadecimal integers + char *csv = sqlite3_malloc(px_count * 17 + 1); + char *ptr = csv; + + for (size_t i = 0; i < px_count; i++) { + ptr += sprintf(ptr, "%lx ", out[i]); + } + + *(--ptr) = '\0'; + sqlite3_free(out); + + sqlite3_result_text(context, csv, -1, sqlite3_free); +} + +int sqlite3_render_init(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi) { + assert(db); + assert(pzErrMsg); + assert(pApi); + + (void)pzErrMsg; + + SQLITE_EXTENSION_INIT2(pApi); + return sqlite3_create_function(db, "eva_render", 4, SQLITE_DETERMINISTIC | SQLITE_INNOCUOUS | SQLITE_UTF8, NULL, eva_render, NULL, NULL); +} diff --git a/core/salis.c b/core/salis.c new file mode 100644 index 0000000..3949059 --- /dev/null +++ b/core/salis.c @@ -0,0 +1,1222 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define DATA_PUSH_BUSY_TIMEOUT 600000 +#define INST_CAP 0x80 +#define INST_MASK 0x7f +#define IPC_FLAG 0x80 +#define MALL_FLAG 0x80 +#define UINT64_HALF 0x8000000000000000ul + +#if defined(COMPRESS) || defined(DATA_PUSH_PATH) +#include "compress.c" +#endif + +struct Proc { +#define PROC_FIELD(type, name) type name; + PROC_FIELDS +#undef PROC_FIELD +}; + +struct Core { + uint64_t cycl; + uint64_t mall; + uint64_t muta[4]; + + uint64_t pnum; + uint64_t pcap; + uint64_t pfst; + uint64_t plst; + uint64_t pcur; + uint64_t psli; + + thrd_t thrd; + uint64_t thrd_idx; + + uint64_t ivpt; + uint64_t *ivav; + uint8_t *iviv; + +#if defined(DATA_PUSH_PATH) + uint64_t emb0; // executions within mb0 counter + uint64_t emb1; // executions within mb1 counter + uint64_t eliv; // executions within not-owned live code counter (parasites) + uint64_t edea; // executions within dead code counter + + uint64_t aeva[MVEC_SIZE]; // allocation events array + uint64_t eeva[MVEC_SIZE]; // execution events array + +#define CORE_DATA_FIELD(type, name, suff) type name suff; + CORE_DATA_FIELDS +#undef CORE_FIELD +#endif + +#define CORE_FIELD(type, name, suff) type name suff; + CORE_FIELDS +#undef CORE_FIELD + + struct Proc *pvec; + uint8_t mvec[MVEC_SIZE]; + uint8_t tgap[THREAD_GAP]; +}; + +// Globals +struct Core g_cores[CORES]; +uint64_t g_steps; +uint64_t g_syncs; +const struct Proc g_dead_proc; + +#if defined(COMMAND_LOAD) || defined(COMMAND_NEW) +char g_asav_pbuf[AUTOSAVE_NAME_LEN]; +#endif + +#if defined(DATA_PUSH_PATH) +sqlite3 *g_sim_data; +#endif + +// Each UI must install these logger functions before salis_init() gets invoked +void (*g_info)(const char *fmt, ...); +void (*g_warn)(const char *fmt, ...); + +// Each architecture must define these functions +#if defined(COMMAND_BENCH) || defined(COMMAND_NEW) +void arch_core_init(struct Core *core); +#endif + +void arch_core_free(struct Core *core); + +#if defined(COMMAND_LOAD) || defined(COMMAND_NEW) +void arch_core_save(FILE *f, const struct Core *core); +#endif + +#if defined(COMMAND_LOAD) +void arch_core_load(FILE *f, struct Core *core); +#endif + +uint64_t arch_proc_mb0_addr(const struct Core *core, uint64_t pix); +uint64_t arch_proc_mb0_size(const struct Core *core, uint64_t pix); +uint64_t arch_proc_mb1_addr(const struct Core *core, uint64_t pix); +uint64_t arch_proc_mb1_size(const struct Core *core, uint64_t pix); +uint64_t arch_proc_ip_addr(const struct Core *core, uint64_t pix); +uint64_t arch_proc_sp_addr(const struct Core *core, uint64_t pix); +uint64_t arch_proc_slice(const struct Core *core, uint64_t pix); +void arch_on_proc_kill(struct Core *core); +void arch_proc_step(struct Core *core, uint64_t pix); + +#if !defined(NDEBUG) +void arch_validate_proc(const struct Core *core, uint64_t pix); +#endif + +wchar_t arch_symbol(uint8_t inst); +const char *arch_mnemonic(uint8_t inst); + +#if defined(DATA_PUSH_PATH) +#if defined(COMMAND_NEW) +void arch_push_data_header(); +#endif +void arch_push_data_line(); +#endif + +// ---------------------------------------------------------------------------- +// Memory vector functions +// ---------------------------------------------------------------------------- +#if defined(MVEC_LOOP) +uint64_t mvec_loop(uint64_t addr) { + return addr % MVEC_SIZE; +} +#endif + +bool mvec_is_alloc(const struct Core *core, uint64_t addr) { + assert(core); + +#if defined(MVEC_LOOP) + return core->mvec[mvec_loop(addr)] & MALL_FLAG ? true : false; +#else + if (addr < MVEC_SIZE) { + return core->mvec[addr] & MALL_FLAG ? true : false; + } else { + return true; + } +#endif +} + +void mvec_alloc(struct Core *core, uint64_t addr) { + assert(core); + assert(!mvec_is_alloc(core, addr)); + +#if defined(MVEC_LOOP) + core->mvec[mvec_loop(addr)] |= MALL_FLAG; +#if defined(DATA_PUSH_PATH) + // Record deallocation event + ++core->aeva[mvec_loop(addr)]; +#endif +#else + assert(addr < MVEC_SIZE); + core->mvec[addr] |= MALL_FLAG; +#if defined(DATA_PUSH_PATH) + // Record deallocation event + ++core->aeva[addr]; +#endif +#endif + core->mall++; +} + +void mvec_free(struct Core *core, uint64_t addr) { + assert(core); + assert(mvec_is_alloc(core, addr)); + +#if defined(MVEC_LOOP) + core->mvec[mvec_loop(addr)] ^= MALL_FLAG; +#if defined(DATA_PUSH_PATH) + // Record deallocation event + ++core->aeva[mvec_loop(addr)]; +#endif +#else + assert(addr < MVEC_SIZE); + core->mvec[addr] ^= MALL_FLAG; +#if defined(DATA_PUSH_PATH) + // Record deallocation event + ++core->aeva[addr]; +#endif +#endif + core->mall--; +} + +uint8_t mvec_get_byte(const struct Core *core, uint64_t addr) { + assert(core); + +#if defined(MVEC_LOOP) + return core->mvec[mvec_loop(addr)]; +#else + if (addr < MVEC_SIZE) { + return core->mvec[addr]; + } else { + return 0; + } +#endif +} + +uint8_t mvec_get_inst(const struct Core *core, uint64_t addr) { + assert(core); + +#if defined(MVEC_LOOP) + return core->mvec[mvec_loop(addr)] & INST_MASK; +#else + if (addr < MVEC_SIZE) { + return core->mvec[addr] & INST_MASK; + } else { + return 0; + } +#endif +} + +void mvec_set_inst(struct Core *core, uint64_t addr, uint8_t inst) { + assert(core); + assert(inst < INST_CAP); + +#if defined(MVEC_LOOP) + core->mvec[mvec_loop(addr)] &= MALL_FLAG; + core->mvec[mvec_loop(addr)] |= inst; +#else + assert(addr < MVEC_SIZE); + core->mvec[addr] &= MALL_FLAG; + core->mvec[addr] |= inst; +#endif +} + +#if defined(MUTA_FLIP) +void mvec_flip_bit(struct Core *core, uint64_t addr, int bit) { + assert(core); + assert(bit < 8); + core->mvec[addr] ^= (1 << bit) & INST_MASK; +} +#endif + +bool mvec_proc_is_live(const struct Core *core, uint64_t pix) { + assert(core); + + return pix >= core->pfst && pix <= core->plst; +} + +bool mvec_is_in_mb0_of_proc(const struct Core *core, uint64_t addr, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + + uint64_t mb0a = arch_proc_mb0_addr(core, pix); + uint64_t mb0s = arch_proc_mb0_size(core, pix); + + return ((addr - mb0a) % MVEC_SIZE) < mb0s; +} + +bool mvec_is_in_mb1_of_proc(const struct Core *core, uint64_t addr, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + + uint64_t mb1a = arch_proc_mb1_addr(core, pix); + uint64_t mb1s = arch_proc_mb1_size(core, pix); + + return ((addr - mb1a) % MVEC_SIZE) < mb1s; +} + +bool mvec_is_proc_owner(const struct Core *core, uint64_t addr, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + return mvec_is_in_mb0_of_proc(core, addr, pix) || mvec_is_in_mb1_of_proc(core, addr, pix); +} + +uint64_t mvec_get_owner(const struct Core *core, uint64_t addr) { + assert(core); + assert(mvec_is_alloc(core, addr)); + + for (uint64_t pix = core->pfst; pix <= core->plst; ++pix) { + if (mvec_is_proc_owner(core, addr, pix)) { + return pix; + } + } + + assert(false); + return -1; +} + +// ---------------------------------------------------------------------------- +// Mutator functions +// ---------------------------------------------------------------------------- +#if defined(COMMAND_BENCH) || defined(COMMAND_NEW) +uint64_t muta_smix(uint64_t *seed) { + assert(seed); + + uint64_t next = (*seed += 0x9e3779b97f4a7c15); + next = (next ^ (next >> 30)) * 0xbf58476d1ce4e5b9; + next = (next ^ (next >> 27)) * 0x94d049bb133111eb; + + return next ^ (next >> 31); +} +#endif + +uint64_t muta_ro64(uint64_t x, int k) { + return (x << k) | (x >> (64 - k)); +} + +uint64_t muta_next(struct Core *core) { + assert(core); + + uint64_t r = muta_ro64(core->muta[1] * 5, 7) * 9; + uint64_t t = core->muta[1] << 17; + + core->muta[2] ^= core->muta[0]; + core->muta[3] ^= core->muta[1]; + core->muta[1] ^= core->muta[2]; + core->muta[0] ^= core->muta[3]; + + core->muta[2] ^= t; + core->muta[3] = muta_ro64(core->muta[3], 45); + + return r; +} + +void muta_cosmic_ray(struct Core *core) { + assert(core); + + uint64_t a = muta_next(core) % MUTA_RANGE; + uint64_t b = muta_next(core); + + if (a < MVEC_SIZE) { +#if defined(MUTA_FLIP) + mvec_flip_bit(core, a, (int)(b % 8)); +#else + mvec_set_inst(core, a, b & INST_MASK); +#endif + } +} + +// ---------------------------------------------------------------------------- +// Process functions +// ---------------------------------------------------------------------------- +void proc_new(struct Core *core, const struct Proc *proc) { + assert(core); + assert(proc); + + if (core->pnum == core->pcap) { + // Reallocate dynamic array + uint64_t new_pcap = core->pcap * 2; + struct Proc *new_pvec = calloc(new_pcap, sizeof(struct Proc)); + + for (uint64_t pix = core->pfst; pix <= core->plst; ++pix) { + uint64_t iold = pix % core->pcap; + uint64_t inew = pix % new_pcap; + memcpy(&new_pvec[inew], &core->pvec[iold], sizeof(struct Proc)); + } + + free(core->pvec); + core->pcap = new_pcap; + core->pvec = new_pvec; + } + + core->pnum++; + core->plst++; + memcpy(&core->pvec[core->plst % core->pcap], proc, sizeof(struct Proc)); +} + +void proc_kill(struct Core *core) { + assert(core); + assert(core->pnum > 1); + + arch_on_proc_kill(core); + + core->pcur++; + core->pfst++; + core->pnum--; +} + +const struct Proc *proc_get(const struct Core *core, uint64_t pix) { + assert(core); + + if (mvec_proc_is_live(core, pix)) { + return &core->pvec[pix % core->pcap]; + } else { + return &g_dead_proc; + } +} + +struct Proc *proc_fetch(struct Core *core, uint64_t pix) { + assert(core); + assert(mvec_proc_is_live(core, pix)); + + return &core->pvec[pix % core->pcap]; +} + +// ---------------------------------------------------------------------------- +// Core functions +// ---------------------------------------------------------------------------- +#if defined(COMMAND_LOAD) || defined(COMMAND_NEW) +void core_save(FILE *f, const struct Core *core) { + assert(f); + assert(core); + + fwrite(&core->cycl, sizeof(uint64_t), 1, f); + fwrite(&core->mall, sizeof(uint64_t), 1, f); + fwrite(core->muta, sizeof(uint64_t), 4, f); + fwrite(&core->pnum, sizeof(uint64_t), 1, f); + fwrite(&core->pcap, sizeof(uint64_t), 1, f); + fwrite(&core->pfst, sizeof(uint64_t), 1, f); + fwrite(&core->plst, sizeof(uint64_t), 1, f); + fwrite(&core->pcur, sizeof(uint64_t), 1, f); + fwrite(&core->psli, sizeof(uint64_t), 1, f); + fwrite(&core->ivpt, sizeof(uint64_t), 1, f); +#if defined(DATA_PUSH_PATH) + fwrite(&core->emb0, sizeof(uint64_t), 1, f); + fwrite(&core->emb1, sizeof(uint64_t), 1, f); + fwrite(&core->eliv, sizeof(uint64_t), 1, f); + fwrite(&core->edea, sizeof(uint64_t), 1, f); +#endif + + fwrite(core->iviv, sizeof(uint8_t), SYNC_INTERVAL, f); + fwrite(core->ivav, sizeof(uint64_t), SYNC_INTERVAL, f); + fwrite(core->pvec, sizeof(struct Proc), core->pcap, f); + fwrite(core->mvec, sizeof(uint8_t), MVEC_SIZE, f); +#if defined(DATA_PUSH_PATH) + fwrite(core->aeva, sizeof(uint64_t), MVEC_SIZE, f); + fwrite(core->eeva, sizeof(uint64_t), MVEC_SIZE, f); +#endif + + arch_core_save(f, core); +} +#endif + +#if defined(COMMAND_BENCH) || defined(COMMAND_NEW) +#if defined(ANC_BYTES) +void core_assemble_ancestor(struct Core *core) { + assert(core); + +#if defined(MVEC_LOOP) + uint64_t addr = UINT64_HALF; +#else + uint64_t addr = 0; +#endif + + uint8_t anc_bytes[] = ANC_BYTES; + + for (uint64_t i = 0; i < sizeof(anc_bytes); ++i, ++addr) { + for (uint64_t j = 0; j < CLONES; ++j) { + uint64_t addr_clone = addr + (MVEC_SIZE / CLONES) * j; + + mvec_alloc(core, addr_clone); + mvec_set_inst(core, addr_clone, anc_bytes[i]); + } + } +} +#endif + +void core_init(struct Core *core, uint64_t *seed) { + assert(core); + assert(seed); + + if (*seed) { + core->muta[0] = muta_smix(seed); + core->muta[1] = muta_smix(seed); + core->muta[2] = muta_smix(seed); + core->muta[3] = muta_smix(seed); + } + + core->pnum = CLONES; + core->pcap = CLONES; + core->plst = CLONES - 1; + core->iviv = calloc(SYNC_INTERVAL, sizeof(uint8_t)); + core->ivav = calloc(SYNC_INTERVAL, sizeof(uint64_t)); + core->pvec = calloc(core->pcap, sizeof(struct Proc)); + + assert(core->iviv); + assert(core->ivav); + assert(core->pvec); + +#if defined(ANC_BYTES) + core_assemble_ancestor(core); + arch_core_init(core); +#endif +} +#endif + +#if defined(COMMAND_LOAD) +void core_load(FILE *f, struct Core *core) { + assert(f); + assert(core); + + fread(&core->cycl, sizeof(uint64_t), 1, f); + fread(&core->mall, sizeof(uint64_t), 1, f); + fread(core->muta, sizeof(uint64_t), 4, f); + fread(&core->pnum, sizeof(uint64_t), 1, f); + fread(&core->pcap, sizeof(uint64_t), 1, f); + fread(&core->pfst, sizeof(uint64_t), 1, f); + fread(&core->plst, sizeof(uint64_t), 1, f); + fread(&core->pcur, sizeof(uint64_t), 1, f); + fread(&core->psli, sizeof(uint64_t), 1, f); + fread(&core->ivpt, sizeof(uint64_t), 1, f); +#if defined(DATA_PUSH_PATH) + fread(&core->emb0, sizeof(uint64_t), 1, f); + fread(&core->emb1, sizeof(uint64_t), 1, f); + fread(&core->eliv, sizeof(uint64_t), 1, f); + fread(&core->edea, sizeof(uint64_t), 1, f); +#endif + + core->iviv = calloc(SYNC_INTERVAL, sizeof(uint8_t)); + core->ivav = calloc(SYNC_INTERVAL, sizeof(uint64_t)); + core->pvec = calloc(core->pcap, sizeof(struct Proc)); + + assert(core->iviv); + assert(core->ivav); + assert(core->pvec); + + fread(core->iviv, sizeof(uint8_t), SYNC_INTERVAL, f); + fread(core->ivav, sizeof(uint64_t), SYNC_INTERVAL, f); + fread(core->pvec, sizeof(struct Proc), core->pcap, f); + fread(core->mvec, sizeof(uint8_t), MVEC_SIZE, f); +#if defined(DATA_PUSH_PATH) + fread(core->aeva, sizeof(uint64_t), MVEC_SIZE, f); + fread(core->eeva, sizeof(uint64_t), MVEC_SIZE, f); +#endif + + arch_core_load(f, core); +} +#endif + +void core_pull_ipcm(struct Core *core) { + assert(core); + assert(core->ivpt < SYNC_INTERVAL); + + uint8_t *iinst = &core->iviv[core->ivpt]; + uint64_t *iaddr = &core->ivav[core->ivpt]; + + if ((*iinst & IPC_FLAG) != 0) { + mvec_set_inst(core, *iaddr, *iinst & INST_MASK); + + *iinst = 0; + *iaddr = 0; + } + + assert(*iinst == 0); + assert(*iaddr == 0); +} + +void core_push_ipcm(struct Core *core, uint8_t inst, uint64_t addr) { + assert(core); + assert(core->ivpt < SYNC_INTERVAL); + assert((inst & IPC_FLAG) == 0); + + uint8_t *iinst = &core->iviv[core->ivpt]; + uint64_t *iaddr = &core->ivav[core->ivpt]; + + assert(*iinst == 0); + assert(*iaddr == 0); + + *iinst = inst | IPC_FLAG; + *iaddr = addr; +} + +void core_step(struct Core *core) { + assert(core); + + if (core->psli != 0) { + core_pull_ipcm(core); + +#if defined(DATA_PUSH_PATH) + // Save execution event locations in database + assert(mvec_proc_is_live(core, core->pcur)); + + uint64_t pcur_ip = arch_proc_ip_addr(core, core->pcur); + + if (mvec_is_in_mb0_of_proc(core, pcur_ip, core->pcur)) { + ++core->emb0; + } else if (mvec_is_in_mb1_of_proc(core, pcur_ip, core->pcur)) { + ++core->emb1; + } else if (mvec_is_alloc(core, pcur_ip)) { + ++core->eliv; + } else { + ++core->edea; + } + +#if defined(MVEC_LOOP) + core->eeva[mvec_loop(pcur_ip)]++; +#else + if (pcur_ip < MVEC_SIZE) { + core->eeva[pcur_ip]++; + } +#endif +#endif + + arch_proc_step(core, core->pcur); + + core->psli--; + core->ivpt++; + + return; + } + + if (core->pcur != core->plst) { + core->psli = arch_proc_slice(core, ++core->pcur); + core_step(core); + return; + } + + core->pcur = core->pfst; + core->psli = arch_proc_slice(core, core->pcur); + core->cycl++; + + // TODO: Implement a day-night cycle + while (core->mall > MVEC_SIZE / 2 && core->pnum > 1) { + proc_kill(core); + } + + muta_cosmic_ray(core); + core_step(core); +} + +// ---------------------------------------------------------------------------- +// Main salis functions +// ---------------------------------------------------------------------------- +#if defined(COMMAND_LOAD) || defined(COMMAND_NEW) +void salis_save(const char *path) { +#if defined(COMPRESS) + size_t size = 0; + char *in = NULL; + FILE *f = open_memstream(&in, &size); +#else + FILE *f = fopen(path, "wb"); +#endif + + assert(f); + + for (int i = 0; i < CORES; ++i) { + core_save(f, &g_cores[i]); + } + + fwrite(&g_steps, sizeof(uint64_t), 1, f); + fwrite(&g_syncs, sizeof(uint64_t), 1, f); + fclose(f); + +#if defined(COMPRESS) + assert(size); + + char *out = malloc(size); + assert(out); + + z_stream strm = { 0 }; + salis_deflate(&strm, size, (Bytef *)in, (Bytef *)out); + + FILE *fx = fopen(path, "wb"); + assert(fx); + + fwrite(&size, sizeof(size_t), 1, fx); + fwrite(out, sizeof(char), strm.total_out, fx); + fclose(fx); + + salis_deflate_end(&strm); + + free(in); + free(out); +#endif +} + +void salis_auto_save() { +#if defined(NDEBUG) + snprintf( +#else + int rem = snprintf( +#endif + g_asav_pbuf, + AUTOSAVE_NAME_LEN, + "%s-%#018lx", + SIM_PATH, + g_steps + ); + + assert(rem >= 0); + assert(rem < AUTOSAVE_NAME_LEN); + + g_info("Saving simulation state on step '%#lx'", g_steps); + salis_save(g_asav_pbuf); +} +#endif + +#if defined(DATA_PUSH_PATH) +void salis_exec_sql(int blob_cnt, const void **blobs, const int *blob_sizes, const char *sql_format, ...) { + assert(sql_format); + + va_list args; + va_start(args, sql_format); + int sql_len = vsnprintf(NULL, 0, sql_format, args) + 1; + char *sql_str = malloc(sql_len); + assert(sql_str); + va_end(args); + + va_start(args, sql_format); + vsprintf(sql_str, sql_format, args); + va_end(args); + + int sql_res; + sqlite3_stmt *sql_stmt; + + sql_res = sqlite3_prepare_v2(g_sim_data, sql_str, -1, &sql_stmt, NULL); + assert(sql_res == SQLITE_OK); + free(sql_str); + + for (int i = 0; i < blob_cnt; ++i) { + assert(blobs[i]); + sql_res = sqlite3_bind_blob(sql_stmt, i + 1, blobs[i], blob_sizes[i], SQLITE_STATIC); + assert(sql_res == SQLITE_OK); + } + + // Only handle SQLITE_BUSY error, in which case we retry the query. + // Setting 'journal_mode=wal;' should help prevent busy database errors. + while (true) { + sql_res = sqlite3_step(sql_stmt); + + if (sql_res == SQLITE_DONE || sql_res == SQLITE_ROW) { + break; + } + + g_warn("SQLite database returned error '%d' with message:", sql_res); + g_warn(sqlite3_errmsg(g_sim_data)); + + if (sql_res == SQLITE_BUSY) { + g_info("Will retry query..."); + continue; + } + + assert(false); + } + + sqlite3_finalize(sql_stmt); +} +#endif + +#if defined(DATA_PUSH_PATH) +#if defined(COMMAND_NEW) +void salis_push_data_header() { + assert(g_sim_data); + + g_info("Creating 'general' table in SQLite database"); + salis_exec_sql( + 0, NULL, NULL, + "create table general (" +#define FOR_CORE(i) \ + "cycl_" #i " int not null, " \ + "mall_" #i " int not null, " \ + "pnum_" #i " int not null, " \ + "pfst_" #i " int not null, " \ + "plst_" #i " int not null, " \ + "amb0_" #i " real not null, " \ + "amb1_" #i " real not null, " \ + "emb0_" #i " int not null, " \ + "emb1_" #i " int not null, " \ + "eliv_" #i " int not null, " \ + "edea_" #i " int not null, " + FOR_CORES +#undef FOR_CORE + "step int not null" + ");" + ); + + // Memory events + char *eprefs[] = { "aev", "eev" }; + int eprefs_cnt = sizeof(eprefs) / sizeof(eprefs[0]); + + for (int i = 0; i < CORES; ++i) { + for (int j = 0; j < eprefs_cnt; ++j) { + g_info("Creating '%s_%d' table in SQLite database", eprefs[j], i); + salis_exec_sql( + 0, NULL, NULL, + "create table %s_%d (" +#define FOR_CORE(i) "cycl_" #i " int not null, " + FOR_CORES +#undef FOR_CORE + "size int not null, " + "evts blob not null ," + "step int not null" + ");", + eprefs[j], i + ); + } + } + + arch_push_data_header(); +} +#endif + +void salis_push_data_line() { + assert(g_sim_data); + + // Measure average membory block sizes + double amb0[CORES] = { 0 }; + double amb1[CORES] = { 0 }; + + for (int i = 0; i < CORES; ++i) { + struct Core *core = &g_cores[i]; + + for (uint64_t j = core->pfst; j <= core->plst; ++j) { + amb0[i] += (double)arch_proc_mb0_size(core, j); + amb1[i] += (double)arch_proc_mb1_size(core, j); + } + + amb0[i] /= core->pnum; + amb1[i] /= core->pnum; + } + + g_info("Pushing row to 'general' table in SQLite database"); + salis_exec_sql( + 0, NULL, NULL, + "insert into general (" +#define FOR_CORE(i) \ + "cycl_" #i ", " \ + "mall_" #i ", " \ + "pnum_" #i ", " \ + "pfst_" #i ", " \ + "plst_" #i ", " \ + "amb0_" #i ", " \ + "amb1_" #i ", " \ + "emb0_" #i ", " \ + "emb1_" #i ", " \ + "eliv_" #i ", " \ + "edea_" #i ", " + FOR_CORES +#undef FOR_CORE + "step" + ") values (" +#define FOR_CORE(i) "%ld, %ld, %ld, %ld, %ld, %f, %f, %ld, %ld, %ld, %ld, " + FOR_CORES +#undef FOR_CORE + "%ld" + ");", +#define FOR_CORE(i) \ + g_cores[i].cycl, \ + g_cores[i].mall, \ + g_cores[i].pnum, \ + g_cores[i].pfst, \ + g_cores[i].plst, \ + amb0[i], \ + amb1[i], \ + g_cores[i].emb0, \ + g_cores[i].emb1, \ + g_cores[i].eliv, \ + g_cores[i].edea, + FOR_CORES +#undef FOR_CORE + g_steps + ); + + // TODO: insert execute memory events + char *eprefs[] = { "aev", "eev" }; + int eprefs_cnt = sizeof(eprefs) / sizeof(eprefs[0]); + + for (int i = 0; i < CORES; ++i) { + for (int j = 0; j < eprefs_cnt; ++j) { + uint64_t *in = NULL; + + if (!strcmp("aev", eprefs[j])) { + in = g_cores[i].aeva; + } else if (!strcmp("eev", eprefs[j])) { + in = g_cores[i].eeva; + } + + // Compress event data + size_t size = sizeof(uint64_t) * MVEC_SIZE; + char *out = malloc(size); + assert(out); + + z_stream strm = { 0 }; + salis_deflate(&strm, size, (Bytef *)in, (Bytef *)out); + + // Insert blob + const void *blob = out; + int blob_size = strm.total_out; + + g_info("Pushing row to '%s_%d' table in SQLite database", eprefs[j], i); + salis_exec_sql( + 1, &blob, &blob_size, + "insert into %s_%d (" +#define FOR_CORE(i) "cycl_" #i ", " + FOR_CORES +#undef FOR_CORE + "size, evts, step" + ") values (" +#define FOR_CORE(i) "%ld, " + FOR_CORES +#undef FOR_CORE + "%ld, ?, %ld" + ");", + eprefs[j], i, +#define FOR_CORE(i) g_cores[i].cycl, + FOR_CORES +#undef FOR_CORE + blob_size, g_steps + ); + + salis_deflate_end(&strm); + free(out); + } + } + + // Reset data aggregation fields + for (int i = 0; i < CORES; ++i) { + struct Core *core = &g_cores[i]; + + core->emb0 = 0; + core->emb1 = 0; + core->eliv = 0; + core->edea = 0; + + memset(core->aeva, 0, sizeof(uint64_t) * MVEC_SIZE); + memset(core->eeva, 0, sizeof(uint64_t) * MVEC_SIZE); + } + + // Push arch-specific data + arch_push_data_line(); +} +#endif + +#if defined(COMMAND_BENCH) || defined(COMMAND_NEW) +void salis_init() { + assert(g_info); + assert(g_warn); + + uint64_t seed = SEED; + + for (int i = 0; i < CORES; ++i) { + core_init(&g_cores[i], &seed); + } + +#if defined(COMMAND_NEW) + salis_auto_save(); +#endif + +#if defined(DATA_PUSH_PATH) + sqlite3_open(DATA_PUSH_PATH, &g_sim_data); + assert(g_sim_data); + + // Install busy handler to retry transactions if DB is locked + sqlite3_busy_timeout(g_sim_data, DATA_PUSH_BUSY_TIMEOUT); + + // Enable Write-Ahead Logging (WAL) + // This seems to help prevent DB locks when displaying live data. + // See: https://sqlite.org/wal.html + salis_exec_sql(0, NULL, NULL, "pragma journal_mode=wal;"); + + // Initialize database + salis_push_data_header(); + salis_push_data_line(); +#endif +} +#endif + +#if defined(COMMAND_LOAD) +void salis_load() { +#if defined(COMPRESS) + FILE *fx = fopen(SIM_PATH, "rb"); + assert(fx); + + fseek(fx, 0, SEEK_END); + size_t x_size = ftell(fx) - sizeof(size_t); + char *in = malloc(x_size); + rewind(fx); + assert(x_size); + assert(in); + + size_t size = 0; + fread(&size, sizeof(size_t), 1, fx); + fread(in, 1, x_size, fx); + fclose(fx); + assert(size); + + char *out = malloc(size); + assert(out); + + z_stream strm = { 0 }; + salis_inflate(&strm, x_size, size, (Bytef *)in, (Bytef *)out); + salis_inflate_end(&strm); + + FILE *f = fmemopen(out, size, "rb"); +#else + FILE *f = fopen(SIM_PATH, "rb"); +#endif + + assert(f); + + for (int i = 0; i < CORES; ++i) { + core_load(f, &g_cores[i]); + } + + fread(&g_steps, sizeof(uint64_t), 1, f); + fread(&g_syncs, sizeof(uint64_t), 1, f); + fclose(f); + +#if defined(COMPRESS) + free(in); + free(out); +#endif + +#if defined(DATA_PUSH_PATH) + sqlite3_open(DATA_PUSH_PATH, &g_sim_data); + assert(g_sim_data); + + // Install busy handler to retry transactions if DB is locked + sqlite3_busy_timeout(g_sim_data, DATA_PUSH_BUSY_TIMEOUT); +#endif +} +#endif + +int salis_thread(struct Core *core) { + assert(core); + + for (uint64_t i = 0; i < core->thrd_idx; ++i) { + core_step(core); + } + + return 0; +} + +void salis_run_thread(uint64_t ns) { + for (int i = 0; i < CORES; ++i) { + g_cores[i].thrd_idx = ns; + + thrd_create( + &g_cores[i].thrd, + (thrd_start_t)salis_thread, + &g_cores[i] + ); + } + + for (int i = 0; i < CORES; ++i) { + thrd_join(g_cores[i].thrd, NULL); + } + + g_steps += ns; +} + +void salis_sync() { + uint8_t *iviv0 = g_cores[0].iviv; + uint64_t *ivav0 = g_cores[0].ivav; + + for (int i = 1; i < CORES; ++i) { + g_cores[i - 1].iviv = g_cores[i].iviv; + g_cores[i - 1].ivav = g_cores[i].ivav; + } + + g_cores[CORES - 1].iviv = iviv0; + g_cores[CORES - 1].ivav = ivav0; + + for (int i = 0; i < CORES; ++i) { + g_cores[i].ivpt = 0; + } + + g_syncs++; +} + +void salis_loop(uint64_t ns, uint64_t dt) { + assert(dt); + + if (ns < dt) { + salis_run_thread(ns); + return; + } + + salis_run_thread(dt); + salis_sync(); + +#if defined(COMMAND_LOAD) || defined(COMMAND_NEW) + if (g_steps % AUTOSAVE_INTERVAL == 0) { + salis_auto_save(); + } +#endif + +#if defined(DATA_PUSH_PATH) + if (g_steps % DATA_PUSH_INTERVAL == 0) { + salis_push_data_line(); + } +#endif + + salis_loop(ns - dt, SYNC_INTERVAL); +} + +#if !defined(NDEBUG) +void salis_validate_core(const struct Core *core) { + assert(core->cycl <= g_steps); + assert(core->plst >= core->pfst); + assert(core->pnum == core->plst + 1 - core->pfst); + assert(core->pnum <= core->pcap); + assert(core->pcur >= core->pfst && core->pcur <= core->plst); + + uint64_t mall = 0; + + for (uint64_t i = 0; i < MVEC_SIZE; ++i) { + mall += mvec_is_alloc(core, i) ? 1 : 0; + } + + assert(core->mall == mall); + + for (uint64_t i = core->pfst; i <= core->plst; ++i) { + arch_validate_proc(core, i); + } + + for (uint64_t i = 0; i < SYNC_INTERVAL; ++i) { + uint8_t iinst = core->iviv[i]; + + if ((iinst & IPC_FLAG) == 0) { + uint64_t iaddr = core->ivav[i]; + + assert(iinst == 0); + assert(iaddr == 0); + } + } + + assert(core->ivpt == g_steps % SYNC_INTERVAL); +} + +void salis_validate() { + assert(g_steps / SYNC_INTERVAL == g_syncs); + + for (int i = 0; i < CORES; ++i) { + salis_validate_core(&g_cores[i]); + } +} +#endif + +void salis_step(uint64_t ns) { + assert(ns); + salis_loop(ns, SYNC_INTERVAL - (g_steps % SYNC_INTERVAL)); + +#if !defined(NDEBUG) + salis_validate(); +#endif +} + +void salis_free() { +#if defined(DATA_PUSH_PATH) + assert(g_sim_data); + sqlite3_close(g_sim_data); +#endif + + for (int i = 0; i < CORES; ++i) { + arch_core_free(&g_cores[i]); + + assert(g_cores[i].pvec); + assert(g_cores[i].iviv); + assert(g_cores[i].ivav); + + free(g_cores[i].pvec); + free(g_cores[i].iviv); + free(g_cores[i].ivav); + + g_cores[i].pvec = NULL; + g_cores[i].iviv = NULL; + g_cores[i].ivav = NULL; + } +} + +// ---------------------------------------------------------------------------- +// Architecture +// ---------------------------------------------------------------------------- +#include "arch.c" + +// ---------------------------------------------------------------------------- +// UI +// ---------------------------------------------------------------------------- +#if defined(COMMAND_LOAD) || defined(COMMAND_NEW) +#include "ui.c" +#endif + +// ---------------------------------------------------------------------------- +// Benchmark +// ---------------------------------------------------------------------------- +#if defined(COMMAND_BENCH) +void log_impl(const char *format, ...) { + va_list args; + va_start(args, format); + vprintf(format, args); + va_end(args); +} + +int main() { + g_info = log_impl; + g_warn = log_impl; + + g_info("Salis Benchmark Test\n\n"); + + salis_init(); + salis_step(STEPS); + + g_info("seed => %#lx\n", SEED); + g_info("g_steps => %#lx\n", g_steps); + g_info("g_syncs => %#lx\n", g_syncs); + + for (int i = 0; i < CORES; ++i) { + g_info("\n"); + g_info("core %d mall => %#lx\n", i, g_cores[i].mall); + g_info("core %d mut0 => %#lx\n", i, g_cores[i].muta[0]); + g_info("core %d mut1 => %#lx\n", i, g_cores[i].muta[1]); + g_info("core %d mut2 => %#lx\n", i, g_cores[i].muta[2]); + g_info("core %d mut3 => %#lx\n", i, g_cores[i].muta[3]); + g_info("core %d pnum => %#lx\n", i, g_cores[i].pnum); + g_info("core %d pcap => %#lx\n", i, g_cores[i].pcap); + g_info("core %d pfst => %#lx\n", i, g_cores[i].pfst); + g_info("core %d plst => %#lx\n", i, g_cores[i].plst); + g_info("core %d pcur => %#lx\n", i, g_cores[i].pcur); + g_info("core %d psli => %#lx\n", i, g_cores[i].psli); + g_info("core %d cycl => %#lx\n", i, g_cores[i].cycl); + g_info("core %d ivpt => %#lx\n", i, g_cores[i].ivpt); + g_info("\n"); + + for (int j = 0; j < 32; ++j) { + g_info("%02x ", g_cores[i].mvec[j]); + } + + g_info("\n"); + } + + salis_free(); +} +#endif diff --git a/data/compress.c b/data/compress.c deleted file mode 100644 index df61123..0000000 --- a/data/compress.c +++ /dev/null @@ -1,56 +0,0 @@ -void salis_deflate(z_stream *strm, size_t size, Bytef *in, Bytef *out) { - assert(strm); - assert(size); - assert(in); - assert(out); - - strm->zalloc = NULL; - strm->zfree = NULL; - strm->opaque = NULL; - - deflateInit(strm, Z_DEFAULT_COMPRESSION); - - strm->avail_in = size; - strm->avail_out = size; - strm->next_in = in; - strm->next_out = out; - - deflate(strm, Z_FINISH); -} - -void salis_deflate_end(z_stream *strm) { - assert(strm); - - deflateEnd(strm); -} - -void salis_inflate(z_stream *strm, size_t avail_in, size_t size, Bytef *in, Bytef *out) { - assert(strm); - assert(avail_in); - assert(size); - assert(in); - assert(out); - - strm->next_in = in; - strm->avail_in = avail_in; - strm->zalloc = NULL; - strm->zfree = NULL; - strm->opaque = NULL; - - inflateInit(strm); - - strm->avail_out = size; - strm->next_out = out; - -#if defined(NDEBUG) - inflate(strm, Z_FINISH); -#else - assert(inflate(strm, Z_FINISH)); -#endif -} - -void salis_inflate_end(z_stream *strm) { - assert(strm); - - inflateEnd(strm); -} diff --git a/data/render.c b/data/render.c deleted file mode 100644 index f9da65d..0000000 --- a/data/render.c +++ /dev/null @@ -1,86 +0,0 @@ -#include -SQLITE_EXTENSION_INIT1 - -#include -#include -#include -#include - -#include "compress.c" - -#define EVA_SIZE (sizeof(uint64_t) * MVEC_SIZE) - -void eva_render(sqlite3_context *context, int argc, sqlite3_value **argv) { - assert(context); - assert(argc == 4); - assert(argv); - - (void)argc; - - size_t left = (size_t)sqlite3_value_int(argv[0]); -#if defined(MVEC_LOOP) - left %= MVEC_SIZE; -#endif - - size_t px_count = (size_t)sqlite3_value_int(argv[1]); - size_t px_pow = (size_t)sqlite3_value_int(argv[2]); - size_t px_res = 1 << px_pow; -#if !defined(MVEC_LOOP) -#if !defined(NDEBUG) - size_t right = left + px_res * px_count; -#endif - assert(left < MVEC_SIZE); - assert(right <= MVEC_SIZE); -#endif - - const void *blob = sqlite3_value_blob(argv[3]); - size_t blob_size = (size_t)sqlite3_value_bytes(argv[3]); - - // Inflate blob - size_t out_size = sizeof(uint64_t) * px_count; - uint64_t *eva = sqlite3_malloc(EVA_SIZE); - uint64_t *out = sqlite3_malloc(out_size); - z_stream strm = { 0 }; - salis_inflate(&strm, blob_size, EVA_SIZE, (Bytef *)blob, (Bytef *)eva); - salis_inflate_end(&strm); - - // Render image - for (size_t i = 0; i < px_count; i++) { - out[i] = 0; - - for (size_t j = 0; j < px_res; j++) { - size_t in_coord = left + i * px_res + j; -#if defined(MVEC_LOOP) - in_coord %= MVEC_SIZE; -#endif - out[i] += eva[in_coord]; - } - } - - sqlite3_free(eva); - - // Transform rendered image into textual representation - // A comma-separated list of hexadecimal integers - char *csv = sqlite3_malloc(px_count * 17 + 1); - char *ptr = csv; - - for (size_t i = 0; i < px_count; i++) { - ptr += sprintf(ptr, "%lx ", out[i]); - } - - *(--ptr) = '\0'; - sqlite3_free(out); - - sqlite3_result_text(context, csv, -1, sqlite3_free); -} - -int sqlite3_render_init(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi) { - assert(db); - assert(pzErrMsg); - assert(pApi); - - (void)pzErrMsg; - - SQLITE_EXTENSION_INIT2(pApi); - return sqlite3_create_function(db, "eva_render", 4, SQLITE_DETERMINISTIC | SQLITE_INNOCUOUS | SQLITE_UTF8, NULL, eva_render, NULL, NULL); -} diff --git a/salis.py b/salis.py index 7167b03..8a174ca 100755 --- a/salis.py +++ b/salis.py @@ -224,7 +224,7 @@ if args.command in ["serve"]: sqlx_defines = set() sqlx_links = set() - sqlx_flags.update({*args.compiler_flags.split(), "-shared", "-fPIC", "-Idata"}) + sqlx_flags.update({*args.compiler_flags.split(), "-shared", "-fPIC", "-Icore"}) sqlx_defines.add(f"-DMVEC_SIZE={2 ** args.mvec_pow}ul") if arch_vars.mvec_loop: sqlx_defines.add("-DMVEC_LOOP") @@ -243,7 +243,7 @@ if args.command in ["serve"]: sqlx_so = os.path.join(sqlx_tempdir.name, "render.so") info("Building salis SQLite extension at:", sqlx_so) - sqlx_build_cmd = [args.compiler, "data/render.c", "-o", sqlx_so] + sqlx_build_cmd = [args.compiler, "core/render.c", "-o", sqlx_so] sqlx_build_cmd.extend(sqlx_flags) sqlx_build_cmd.extend(sqlx_defines) sqlx_build_cmd.extend(sqlx_links) @@ -523,7 +523,7 @@ info("Created a temporary salis directory at:", tempdir.name) salis_bin = os.path.join(tempdir.name, "salis_bin") info("Building salis binary at:", salis_bin) -build_cmd = [args.compiler, "core.c", "-o", salis_bin] +build_cmd = [args.compiler, "core/salis.c", "-o", salis_bin] build_cmd.extend(flags) build_cmd.extend(sum(map(lambda include: ["-include", include], includes), [])) build_cmd.extend(defines) -- cgit v1.2.1