From 9779eecbee025d757df43b03a0dc27c5148245a5 Mon Sep 17 00:00:00 2001 From: Paul Oliver Date: Wed, 3 Apr 2024 03:11:42 +0200 Subject: Adds salis-v1 VM architecture --- src/arch/salis-v1.c | 860 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 860 insertions(+) create mode 100644 src/arch/salis-v1.c (limited to 'src/arch/salis-v1.c') diff --git a/src/arch/salis-v1.c b/src/arch/salis-v1.c new file mode 100644 index 0000000..0e8f202 --- /dev/null +++ b/src/arch/salis-v1.c @@ -0,0 +1,860 @@ +// Project: Salis +// Author: Paul Oliver +// Email: contact@pauloliver.dev + +/* + * This is based on the original VM architecture in salis-v1: + * https://git.pauloliver.dev/salis-v1/about/ + */ + +bool mvec_is_alloc(const Core *core, u64 pix); +void mvec_alloc(Core *core, u64 addr); +void mvec_free(Core *core, u64 addr); +u8 mvec_get_inst(const Core *core, u64 addr); +void mvec_set_inst(Core *core, u64 addr, u8 inst); +bool mvec_is_proc_owner(const Core *core, u64 addr, u64 pix); +void proc_new(Core *core, const Proc *proc); +bool proc_is_live(const Core *core, u64 pix); +const Proc *proc_get(const Core *core, u64 pix); +Proc *proc_fetch(Core *core, u64 pix); + +#define INST_LIST \ + INST(noop, L' ') \ + INST(nop0, L'0') \ + INST(nop1, L'1') \ + INST(nop2, L'2') \ + INST(nop3, L'3') \ + \ + INST(jmpb, L'(') \ + INST(jmpf, L')') \ + INST(adrb, L'[') \ + INST(adrf, L']') \ + INST(ifnz, L'?') \ + \ + INST(allb, L'{') \ + INST(allf, L'}') \ + INST(bswp, L'%') \ + INST(bclr, L'|') \ + INST(splt, L'$') \ + \ + INST(addn, L'+') \ + INST(subn, L'-') \ + INST(muln, L'*') \ + INST(divn, L'/') \ + INST(incn, L'^') \ + INST(decn, L'v') \ + INST(notn, L'!') \ + INST(shfl, L'<') \ + INST(shfr, L'>') \ + INST(zero, L'z') \ + INST(unit, L'u') \ + \ + INST(pshn, L'#') \ + INST(popn, L'~') \ + \ + INST(load, L'.') \ + INST(wrte, L':') \ + INST(dupl, L'"') \ + INST(swap, L'x') \ + \ + INST(keya, L'a') \ + INST(keyb, L'b') \ + INST(keyc, L'c') \ + INST(keyd, L'd') \ + INST(keye, L'e') \ + INST(keyf, L'f') \ + INST(keyg, L'g') \ + INST(keyh, L'h') \ + INST(keyi, L'i') \ + INST(keyj, L'j') \ + INST(keyk, L'k') \ + INST(keyl, L'l') \ + INST(keym, L'm') \ + INST(keyn, L'n') \ + INST(keyo, L'o') \ + INST(keyp, L'p') \ + \ + INST(loka, L'A') \ + INST(lokb, L'B') \ + INST(lokc, L'C') \ + INST(lokd, L'D') \ + INST(loke, L'E') \ + INST(lokf, L'F') \ + INST(lokg, L'G') \ + INST(lokh, L'H') \ + INST(loki, L'I') \ + INST(lokj, L'J') \ + INST(lokk, L'K') \ + INST(lokl, L'L') \ + INST(lokm, L'M') \ + INST(lokn, L'N') \ + INST(loko, L'O') \ + INST(lokp, L'P') + +#define MNEMONIC_BUFF_SIZE (0x5) + +enum sinst { +#define INST(name, symb) name, + INST_LIST +#undef INST + INST_COUNT +}; + +#define PROC_FIELDS \ + PROC_FIELD(u64, ip) \ + PROC_FIELD(u64, sp) \ + PROC_FIELD(u64, mb0a) \ + PROC_FIELD(u64, mb0s) \ + PROC_FIELD(u64, mb1a) \ + PROC_FIELD(u64, mb1s) \ + PROC_FIELD(u64, r0x) \ + PROC_FIELD(u64, r1x) \ + PROC_FIELD(u64, r2x) \ + PROC_FIELD(u64, r3x) \ + PROC_FIELD(u64, s0) \ + PROC_FIELD(u64, s1) \ + PROC_FIELD(u64, s2) \ + PROC_FIELD(u64, s3) \ + PROC_FIELD(u64, s4) \ + PROC_FIELD(u64, s5) \ + PROC_FIELD(u64, s6) \ + PROC_FIELD(u64, s7) + +struct Proc { +#define PROC_FIELD(type, name) type name; + PROC_FIELDS +#undef PROC_FIELD +}; + +u64 arch_proc_mb0_addr(const Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + return proc_get(core, pix)->mb0a; +} + +u64 arch_proc_mb0_size(const Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + return proc_get(core, pix)->mb0s; +} + +u64 arch_proc_mb1_addr(const Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + return proc_get(core, pix)->mb1a; +} + +u64 arch_proc_mb1_size(const Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + return proc_get(core, pix)->mb1s; +} + +u64 arch_proc_ip_addr(const Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + return proc_get(core, pix)->ip; +} + +u64 arch_proc_sp_addr(const Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + return proc_get(core, pix)->sp; +} + +u64 arch_proc_slice(const Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + (void)core; + (void)pix; + + return 1; +} + +void _free_memory_block(Core *core, u64 addr, u64 size) { + assert(core); + assert(size); + + for (u64 i = 0; i < size; ++i) { + mvec_free(core, addr + i); + } +} + +void arch_on_proc_kill(Core *core) { + assert(core); + assert(core->pnum > 1); + + 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_dead_proc, sizeof(Proc)); +} + +#if ACTION == ACT_BENCH || ACTION == ACT_NEW +void arch_anc_init(Core *core, u64 size) { + assert(core); + + proc_fetch(core, 0)->mb0s = size; +} +#endif + +u8 _get_inst(const Core *core, u64 addr) { + assert(core); + + return mvec_get_inst(core, addr) % INST_COUNT; +} + +void _increment_ip(Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + Proc *proc = proc_fetch(core, pix); + + proc->ip++; + proc->sp = proc->ip; +} + +bool _is_between(u8 inst, u8 lo, u8 hi) { + assert(inst < INST_COUNT); + assert(lo < INST_COUNT); + assert(hi < INST_COUNT); + assert(lo < hi); + + return (inst >= lo) && (inst <= hi); +} + +bool _is_key(u8 inst) { + assert(inst < INST_COUNT); + + return _is_between(inst, keya, keyp); +} + +bool _is_lock(u8 inst) { + assert(inst < INST_COUNT); + + return _is_between(inst, loka, lokp); +} + +bool _is_rmod(u8 inst) { + assert(inst < INST_COUNT); + + return _is_between(inst, nop0, nop3); +} + +bool _key_lock_match(u8 key, u8 lock) { + assert(key < INST_COUNT); + assert(lock < INST_COUNT); + assert(_is_key(key)); + + return (key - keya) == (lock - loka); +} + +bool _seek(Core *core, u64 pix, bool fwrd) { + assert(core); + assert(proc_is_live(core, pix)); + + Proc *proc = proc_fetch(core, pix); + u8 next = _get_inst(core, proc->ip + 1); + + if (!_is_key(next)) { + _increment_ip(core, pix); + return false; + } + + u8 spin = _get_inst(core, proc->sp); + + if (_key_lock_match(next, spin)) { + return true; + } + + if (fwrd) { + proc->sp++; + } else { + proc->sp--; + } + + return false; +} + +void _jump(Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + Proc *proc = proc_fetch(core, pix); + +#ifndef NDEBUG + u8 next = _get_inst(core, proc->ip + 1); + u8 spin = _get_inst(core, proc->sp); + assert(_is_key(next)); + assert(_is_lock(spin)); + assert(_key_lock_match(next, spin)); +#endif + + proc->ip = proc->sp; +} + +void _get_reg_addr_list(Core *core, u64 pix, u64 **rlist, int rcount, bool offset) { + assert(core); + assert(proc_is_live(core, pix)); + + assert(rlist); + assert(rcount); + assert(rcount < 4); + + Proc *proc = proc_fetch(core, pix); + u64 madr = proc->ip + (offset ? 2 : 1); + + for (int i = 0; i < rcount; ++i) { + rlist[i] = &proc->r0x; + } + + for (int i = 0; i < rcount; ++i) { + u64 mnxt = madr + i; + u8 mins = _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; + } + } +} + +void _addr(Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + Proc *proc = proc_fetch(core, pix); + u64 *reg; + +#ifndef NDEBUG + u8 next = _get_inst(core, proc->ip + 1); + u8 spin = _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); +} + +void _ifnz(Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + Proc *proc = proc_fetch(core, pix); + u64 *reg; + + _get_reg_addr_list(core, pix, ®, 1, false); + + u64 jmod = _is_rmod(_get_inst(core, proc->ip + 1)) ? 1 : 0; + u64 rmod = *reg ? 1 : 2; + + proc->ip += jmod + rmod; + proc->sp = proc->ip; +} + +void _free_child_memory_of(Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + Proc *proc = proc_fetch(core, pix); + + assert(proc->mb1s); + + _free_memory_block(core, proc->mb1a, proc->mb1s); + + proc->mb1a = 0; + proc->mb1s = 0; +} + +void _alloc(Core *core, u64 pix, bool fwrd) { + assert(core); + assert(proc_is_live(core, pix)); + + Proc *proc = proc_fetch(core, pix); + u64 *regs[2]; + + _get_reg_addr_list(core, pix, regs, 2, false); + + u64 bsize = *regs[0]; + + // do nothing if block-size is zero + if (!bsize) { + _increment_ip(core, pix); + return; + } + + // do nothing if sp is not adjacent to allocated mem. block + if (proc->mb1s) { + u64 exp_addr = proc->mb1a; + + if (fwrd) { + exp_addr += proc->mb1s; + } else { + exp_addr--; + } + + if (proc->sp != exp_addr) { + _increment_ip(core, pix); + return; + } + } + + // allocation was successful, store block address on register + if (proc->mb1s == bsize) { + _increment_ip(core, pix); + *regs[1] = proc->mb1a; + return; + } + + // sp collided with another allocated block, clear and try again + if (mvec_is_alloc(core, proc->sp)) { + if (proc->mb1s) { + _free_child_memory_of(core, pix); + } + + if (fwrd) { + proc->sp++; + } else { + proc->sp--; + } + + return; + } + + // otherwise enlarge block + mvec_alloc(core, proc->sp); + + // adjust child block address and size + if (!proc->mb1s || !fwrd) { + proc->mb1a = proc->sp; + } + + proc->mb1s++; + + // move sp to new location + if (fwrd) { + proc->sp++; + } else { + proc->sp--; + } +} + +void _bswap(Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + Proc *proc = proc_fetch(core, pix); + + if (proc->mb1s) { + u64 tmpa = proc->mb0a; + u64 tmps = proc->mb0s; + + proc->mb0a = proc->mb1a; + proc->mb0s = proc->mb1s; + proc->mb1a = tmpa; + proc->mb1s = tmps; + } + + _increment_ip(core, pix); +} + +void _bclear(Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + Proc *proc = proc_fetch(core, pix); + + if (proc->mb1s) { + _free_child_memory_of(core, pix); + } + + _increment_ip(core, pix); +} + +void _split(Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + Proc *proc = proc_fetch(core, pix); + + if (proc->mb1s) { + 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; + + proc_new(core, &child); + } else { + assert(!proc->mb1a); + } + + _increment_ip(core, pix); +} + +void _3rop(Core *core, u64 pix, u8 inst) { + assert(core); + assert(proc_is_live(core, pix)); + + u64 *regs[3]; + + _get_reg_addr_list(core, pix, regs, 3, false); + + 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: + // do nothing on div. by zero + if (*regs[2]) { + *regs[0] = *regs[1] / *regs[2]; + } + + break; + default: + assert(false); + } + + _increment_ip(core, pix); +} + +void _1rop(Core *core, u64 pix, u8 inst) { + assert(core); + assert(proc_is_live(core, pix)); + + u64 *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); +} + +void _push(Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + Proc *proc = proc_fetch(core, pix); + u64 *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); +} + +void _pop(Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + Proc *proc = proc_fetch(core, pix); + u64 *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); +} + +int _sp_dir(u64 src, u64 dst) { + if (src == dst) { + return 0; + } else if (src - dst <= dst - src) { + return -1; + } else { + return 1; + } +} + +void _load(Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + Proc *proc = proc_fetch(core, pix); + u64 *regs[2]; + + _get_reg_addr_list(core, pix, regs, 2, false); + + int sp_dir = _sp_dir(proc->sp, *regs[0]); + + 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); + } +} + +bool _is_writeable_by(const Core *core, u64 addr, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + return !mvec_is_alloc(core, addr) || mvec_is_proc_owner(core, addr, pix); +} + +void _write(Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + Proc *proc = proc_fetch(core, pix); + u64 *regs[2]; + + _get_reg_addr_list(core, pix, regs, 2, false); + + int sp_dir = _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)) { + mvec_set_inst(core, *regs[0], *regs[1] % INST_CAPS); + } + + _increment_ip(core, pix); + } +} + +void _2rop(Core *core, u64 pix, u8 inst) { + assert(core); + assert(proc_is_live(core, pix)); + + u64 *regs[2]; + + _get_reg_addr_list(core, pix, regs, 2, false); + + switch (inst) { + case dupl: + *regs[1] = *regs[0]; + break; + case swap: + { + u64 tmp = *regs[0]; + *regs[0] = *regs[1]; + *regs[1] = tmp; + } + + break; + default: + assert(false); + } + + _increment_ip(core, pix); +} + +void arch_proc_step(Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + Proc *proc = proc_fetch(core, pix); + u8 inst = _get_inst(core, proc->ip); + + 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)) { + _addr(core, pix); + } + + break; + case adrf: + if (_seek(core, pix, true)) { + _addr(core, pix); + } + + break; + case ifnz: + _ifnz(core, pix); + break; + case allb: + _alloc(core, pix, false); + break; + case allf: + _alloc(core, pix, true); + break; + case bswp: + _bswap(core, pix); + break; + case bclr: + _bclear(core, pix); + break; + case splt: + _split(core, pix); + break; + case addn: + case subn: + case muln: + case divn: + _3rop(core, pix, inst); + break; + case incn: + case decn: + case notn: + case shfl: + case shfr: + case zero: + case unit: + _1rop(core, pix, inst); + break; + case pshn: + _push(core, pix); + break; + case popn: + _pop(core, pix); + break; + case load: + _load(core, pix); + break; + case wrte: + _write(core, pix); + break; + case dupl: + case swap: + _2rop(core, pix, inst); + break; + default: + _increment_ip(core, pix); + break; + } + + return; +} + +#ifndef NDEBUG +void arch_validate_proc(const Core *core, u64 pix) { + assert(core); + + const Proc *proc = proc_get(core, pix); + + assert(proc->mb0s); + + if (proc->mb1a) { + assert(proc->mb1s); + } + + for (u64 i = 0; i < proc->mb0s; ++i) { + u64 addr = proc->mb0a + i; + assert(mvec_is_alloc(core, addr)); + assert(mvec_is_proc_owner(core, addr, pix)); + } + + for (u64 i = 0; i < proc->mb1s; ++i) { + u64 addr = proc->mb1a + i; + assert(mvec_is_alloc(core, addr)); + assert(mvec_is_proc_owner(core, addr, pix)); + } +} +#endif + +wchar_t arch_symbol(u8 inst) { + switch (inst % INST_COUNT) { +#define INST(name, symb) case name: return symb; + INST_LIST +#undef INST + } + + assert(false); + return L'\0'; +} + +void arch_mnemonic(u8 inst, char *buff) { + assert(buff); + + switch (inst % INST_COUNT) { +#define INST(name, symb) case name: snprintf(buff, MNEMONIC_BUFF_SIZE, #name); return; + INST_LIST +#undef INST + } + + assert(false); +} -- cgit v1.2.1