#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 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; #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) void arch_push_data_header(); 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; #else assert(addr < MVEC_SIZE); core->mvec[addr] |= MALL_FLAG; #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; #else assert(addr < MVEC_SIZE); core->mvec[addr] ^= MALL_FLAG; #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); 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); 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); 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); 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); 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 }; 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 = (Bytef *)in; strm.next_out = (Bytef *)out; deflate(&strm, Z_FINISH); FILE *fx = fopen(path, "wb"); assert(fx); fwrite(&size, sizeof(size_t), 1, fx); fwrite(out, sizeof(char), strm.total_out, fx); fclose(fx); deflateEnd(&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(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;"); arch_push_data_header(); arch_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 }; strm.next_in = (Bytef *)in; strm.avail_in = x_size; strm.zalloc = NULL; strm.zfree = NULL; strm.opaque = NULL; inflateInit(&strm); strm.avail_out = size; strm.next_out = (Bytef *)out; #if defined(NDEBUG) inflate(&strm, Z_FINISH); #else assert(inflate(&strm, Z_FINISH)); #endif inflateEnd(&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) { arch_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