SilverSight/cpp/AVMIsa/avm.hpp
allaun f6cbddcbf2 feat(tests): Python AVM port rewrite + test harness, Go test harness
Python port rewritten to match spec:
- Added Q0_16, PUSH_Q0, PUSH_BOOL as separate opcodes
- Added V6 comparison (lt_q16_v6)
- Added floor division (Lean Int.ediv)
- Added stack depth limit (AVM_MAX_STACK = 1024)
- Added type checking in exec_prim
- All 10 tests passing

Go AVM port: added test_avm_test.go with 8 test cases

Milestone: Python → , Go → 🔄
2026-06-30 17:56:09 -05:00

144 lines
5.4 KiB
C++

// AVM ISA v1 — C++ Port (Strict Functional Execution)
#include <cstdint>
#include <vector>
#include <optional>
#include <variant>
#include <stdexcept>
#include <functional>
constexpr int32_t Q16_SCALE = 65536;
inline int32_t q16_mul(int32_t a, int32_t b) {
return static_cast<int32_t>(
(static_cast<int64_t>(a) * static_cast<int64_t>(b)) / Q16_SCALE);
}
inline int32_t q16_div(int32_t a, int32_t b) {
if (b == 0) throw std::runtime_error("div by zero");
return static_cast<int32_t>(
(static_cast<int64_t>(a) * Q16_SCALE) / b);
}
// ── Value ─────────────────────────────────────────
struct AvmVal {
enum Ty { Q0_16, Q16_16, Bool };
Ty ty;
int32_t raw;
AvmVal(Ty t, int32_t r) : ty(t), raw(r) {}
static AvmVal q16(int32_t x) { return {Q16_16, x}; }
static AvmVal q0(int32_t x) {
if (x < -32767) x = -32767;
if (x > 32767) x = 32767;
return {Q0_16, x};
}
static AvmVal boolean(bool x) { return {Bool, x ? 1 : 0}; }
};
// ── Primitives ─────────────────────────────────────
enum class Prim : int32_t {
AddSatQ0, SubSatQ0, AddSatQ16, SubSatQ16, MulSatQ16, DivSatQ16,
LtQ16, EqQ16, And, Or, Not
};
AvmVal exec_prim(Prim op, const AvmVal& a, const std::optional<AvmVal>& b) {
auto bv = [&]() -> const AvmVal& { return b.value(); };
switch (op) {
case Prim::AddSatQ16: return AvmVal::q16(a.raw + bv().raw);
case Prim::SubSatQ16: return AvmVal::q16(a.raw - bv().raw);
case Prim::MulSatQ16: return AvmVal::q16(q16_mul(a.raw, bv().raw));
case Prim::DivSatQ16: return AvmVal::q16(q16_div(a.raw, bv().raw));
case Prim::LtQ16: return AvmVal::boolean(a.raw < bv().raw);
case Prim::EqQ16: return AvmVal::boolean(a.raw == bv().raw);
case Prim::And: return AvmVal::boolean(a.raw && bv().raw);
case Prim::Or: return AvmVal::boolean(a.raw || bv().raw);
case Prim::Not: return AvmVal::boolean(!a.raw);
default: throw std::runtime_error("unknown prim");
}
}
// ── Instruction ────────────────────────────────────
enum class InstrOp : int8_t {
PushQ16, PushBool, Pop, Dup, Swap, Load, Store, Jump, JumpIf, Prim, Halt
};
struct Instr {
InstrOp op;
int32_t arg;
bool arg2{false};
};
inline Instr push_q16(int32_t x) { return {InstrOp::PushQ16, x, false}; }
inline Instr push_bool(bool x) { return {InstrOp::PushBool, static_cast<int32_t>(x), true}; }
inline Instr pop() { return {InstrOp::Pop, 0, false}; }
inline Instr dup() { return {InstrOp::Dup, 0, false}; }
inline Instr swap() { return {InstrOp::Swap, 0, false}; }
inline Instr load(int i) { return {InstrOp::Load, i, false}; }
inline Instr store(int i) { return {InstrOp::Store, i, false}; }
inline Instr jump(int t) { return {InstrOp::Jump, t, false}; }
inline Instr jump_if(int t) { return {InstrOp::JumpIf, t, false}; }
inline Instr prim(Prim p) { return {InstrOp::Prim, static_cast<int32_t>(p), false}; }
inline Instr halt() { return {InstrOp::Halt, 0, false}; }
// ── State ──────────────────────────────────────────
struct State {
int pc{0};
std::vector<AvmVal> stack;
std::vector<std::optional<AvmVal>> locals;
bool halted{false};
State() : pc(0), locals(16, std::nullopt) {}
State(int pc_, std::vector<AvmVal> stack_, std::vector<std::optional<AvmVal>> locals_, bool halted_)
: pc(pc_), stack(std::move(stack_)), locals(std::move(locals_)), halted(halted_) {}
};
// ── Step ───────────────────────────────────────────
State step(const State& s, const std::vector<Instr>& prog) {
if (s.halted) throw std::runtime_error("halted");
if (s.pc < 0 || s.pc >= (int)prog.size())
return State{};
auto instr = prog[s.pc];
State ns{s.pc + 1, s.stack, s.locals, false};
auto& st = ns.stack;
switch (instr.op) {
case InstrOp::PushQ16: st.push_back(AvmVal::q16(instr.arg)); break;
case InstrOp::PushBool: st.push_back(AvmVal::boolean(instr.arg)); break;
case InstrOp::Pop: st.pop_back(); break;
case InstrOp::Dup: st.push_back(st.back()); break;
case InstrOp::Swap: std::swap(st[st.size()-2], st[st.size()-1]); break;
case InstrOp::Load: {
auto v = ns.locals[instr.arg];
if (!v.has_value()) throw std::runtime_error("missing local");
st.push_back(v.value());
break;
}
case InstrOp::Store: {
ns.locals[instr.arg] = st.back(); st.pop_back();
break;
}
case InstrOp::Jump: ns.pc = instr.arg; break;
case InstrOp::JumpIf: {
if (st.back().raw) ns.pc = instr.arg;
st.pop_back();
break;
}
case InstrOp::Prim: {
auto p = static_cast<Prim>(instr.arg);
int arity = (p == Prim::Not) ? 1 : 2;
std::optional<AvmVal> b;
if (arity == 2) { b = st.back(); st.pop_back(); }
auto a = st.back(); st.pop_back();
st.push_back(exec_prim(p, a, b));
break;
}
case InstrOp::Halt: ns.halted = true; break;
}
return ns;
}
State run(const State& init, const std::vector<Instr>& prog, int fuel) {
State s = init;
for (int i = 0; i < fuel && !s.halted; i++)
s = step(s, prog);
return s;
}