// AVM ISA v1 — C++ Port (Strict Functional Execution) #pragma once #include #include #include #include #include namespace avm { // ── Constants ──────────────────────────────────────────────────── constexpr int32_t AVM_CLAMP_MIN = -2147483647; constexpr int32_t AVM_CLAMP_MAX = 2147483647; constexpr int32_t AVM_Q0_MIN = -32767; constexpr int32_t AVM_Q0_MAX = 32767; constexpr int64_t Q16_SCALE = 65536; constexpr size_t AVM_MAX_STACK = 1024; inline int32_t avm_clamp(int64_t x) { if (x > AVM_CLAMP_MAX) return AVM_CLAMP_MAX; if (x < AVM_CLAMP_MIN) return AVM_CLAMP_MIN; return static_cast(x); } inline int32_t avm_q0_clamp(int64_t x) { if (x > AVM_Q0_MAX) return AVM_Q0_MAX; if (x < AVM_Q0_MIN) return AVM_Q0_MIN; return static_cast(x); } inline int32_t floor_div(int64_t a, int64_t b) { if (b == 0) throw std::runtime_error("division by zero"); int64_t q = a / b; if (a % b != 0 && ((a ^ b) < 0)) q--; return static_cast(q); } inline bool lt_q16_v6(int32_t a, int32_t b) { bool sa = a < 0, sb = b < 0; return (sa != sb) ? sa : (a < b); } // ── Types ────────────────────────────────────────────────────── enum class Ty : uint8_t { Q0_16, Q16_16, Bool }; using Val = std::variant; struct AnyVal { Ty ty; Val val; }; // ── Primitives ──────────────────────────────────────────────── enum class Prim : uint8_t { AddSatQ0, SubSatQ0, AddSatQ16, SubSatQ16, MulSatQ16, DivSatQ16, LtQ16, EqQ16, And, Or, Not }; inline int prim_arity(Prim p) { return (p == Prim::Not) ? 1 : 2; } // ── Instructions ────────────────────────────────────────────── enum class Op : uint8_t { PushQ16, PushBool, PushQ0, Pop, Dup, Swap, Load, Store, Jump, JumpIf, Primitive, Halt }; struct Instr { Op op; int32_t arg; bool arg2; }; // ── Primitive execution ────────────────────────────────────── inline AnyVal exec_prim(Prim p, const AnyVal& a, const AnyVal& b) { auto check = [](const AnyVal& v, Ty t) { if (v.ty != t) throw std::runtime_error("type mismatch"); }; switch (p) { case Prim::AddSatQ0: check(a, Ty::Q0_16); check(b, Ty::Q0_16); return {Ty::Q0_16, avm_q0_clamp(static_cast(std::get(a.val)) + std::get(b.val))}; case Prim::SubSatQ0: check(a, Ty::Q0_16); check(b, Ty::Q0_16); return {Ty::Q0_16, avm_q0_clamp(static_cast(std::get(a.val)) - std::get(b.val))}; case Prim::AddSatQ16: check(a, Ty::Q16_16); check(b, Ty::Q16_16); return {Ty::Q16_16, avm_clamp(static_cast(std::get(a.val)) + std::get(b.val))}; case Prim::SubSatQ16: check(a, Ty::Q16_16); check(b, Ty::Q16_16); return {Ty::Q16_16, avm_clamp(static_cast(std::get(a.val)) - std::get(b.val))}; case Prim::MulSatQ16: check(a, Ty::Q16_16); check(b, Ty::Q16_16); return {Ty::Q16_16, avm_clamp(floor_div(static_cast(std::get(a.val)) * std::get(b.val), Q16_SCALE))}; case Prim::DivSatQ16: check(a, Ty::Q16_16); check(b, Ty::Q16_16); return {Ty::Q16_16, avm_clamp(floor_div(static_cast(std::get(a.val)) * Q16_SCALE, std::get(b.val)))}; case Prim::LtQ16: check(a, Ty::Q16_16); check(b, Ty::Q16_16); return {Ty::Bool, lt_q16_v6(std::get(a.val), std::get(b.val))}; case Prim::EqQ16: check(a, Ty::Q16_16); check(b, Ty::Q16_16); return {Ty::Bool, std::get(a.val) == std::get(b.val)}; case Prim::And: check(a, Ty::Bool); check(b, Ty::Bool); return {Ty::Bool, std::get(a.val) && std::get(b.val)}; case Prim::Or: check(a, Ty::Bool); check(b, Ty::Bool); return {Ty::Bool, std::get(a.val) || std::get(b.val)}; case Prim::Not: check(a, Ty::Bool); return {Ty::Bool, !std::get(a.val)}; } throw std::runtime_error("unknown prim"); } // ── State ───────────────────────────────────────────────────── struct State { int pc = 0; std::vector stack; std::vector> locals; bool halted = false; }; inline State init_state(size_t n_locals = 0) { return {0, {}, std::vector>(n_locals), false}; } // ── Step ───────────────────────────────────────────────────── inline std::optional step(const State& s, const std::vector& prog) { if (s.halted) return std::nullopt; if (s.pc < 0 || static_cast(s.pc) >= prog.size()) return State{s.pc, s.stack, s.locals, true}; auto instr = prog[s.pc]; State ns = s; int npc = s.pc + 1; auto growing = (instr.op == Op::PushQ16 || instr.op == Op::PushBool || instr.op == Op::PushQ0 || instr.op == Op::Dup || instr.op == Op::Load); if (growing && ns.stack.size() >= AVM_MAX_STACK) return std::nullopt; // overflow switch (instr.op) { case Op::PushQ16: ns.stack.push_back({Ty::Q16_16, avm_clamp(instr.arg)}); break; case Op::PushBool: ns.stack.push_back({Ty::Bool, instr.arg2}); break; case Op::PushQ0: ns.stack.push_back({Ty::Q0_16, avm_q0_clamp(instr.arg)}); break; case Op::Pop: if (ns.stack.empty()) return std::nullopt; ns.stack.pop_back(); break; case Op::Dup: if (ns.stack.empty()) return std::nullopt; ns.stack.push_back(ns.stack.back()); break; case Op::Swap: if (ns.stack.size() < 2) return std::nullopt; std::swap(ns.stack[ns.stack.size()-1], ns.stack[ns.stack.size()-2]); break; case Op::Load: { size_t i = instr.arg; if (i >= ns.locals.size() || !ns.locals[i].has_value()) return std::nullopt; ns.stack.push_back(ns.locals[i].value()); break; } case Op::Store: { size_t i = instr.arg; if (ns.stack.empty() || i >= ns.locals.size()) return std::nullopt; ns.locals[i] = ns.stack.back(); ns.stack.pop_back(); break; } case Op::Jump: if (instr.arg < 0 || static_cast(instr.arg) >= prog.size()) return std::nullopt; npc = instr.arg; break; case Op::JumpIf: { if (ns.stack.empty()) return std::nullopt; auto v = ns.stack.back(); ns.stack.pop_back(); if (v.ty != Ty::Bool) return std::nullopt; if (std::get(v.val)) { if (instr.arg < 0 || static_cast(instr.arg) >= prog.size()) return std::nullopt; npc = instr.arg; } break; } case Op::Primitive: { auto p = static_cast(instr.arg); int arity = prim_arity(p); if (static_cast(ns.stack.size()) < arity) return std::nullopt; AnyVal b{Ty::Bool, false}; if (arity >= 2) { b = ns.stack.back(); ns.stack.pop_back(); } AnyVal a = ns.stack.back(); ns.stack.pop_back(); try { ns.stack.push_back(exec_prim(p, a, b)); } catch (...) { return std::nullopt; } break; } case Op::Halt: ns.halted = true; break; } ns.pc = npc; return ns; } // ── Run (fuel-bounded) ─────────────────────────────────────── inline std::optional run(const State& init, const std::vector& prog, int fuel = 10000) { State s = init; for (int i = 0; i < fuel; i++) { if (s.halted) return s; auto next = step(s, prog); if (!next.has_value()) return std::nullopt; s = next.value(); } return s; } } // namespace avm