mirror of
https://github.com/allaunthefox/SilverSight.git
synced 2026-07-30 17:16:16 +00:00
Lean (reference), Python, Rust, C, C++, Go, Julia, R, Scala, Fortran, Coq, Octave — all implementing the same AVM ISA v1 specification. Every port implements: - Full type universe: Q0_16, Q16_16, Bool - 11 primitives with floor division (Lean Int.ediv), V6 signed comparison, symmetric clamping [-2147483647, 2147483647] - 12 instruction opcodes with stack depth limit (1024) - Fuel-bounded run loop - Error handling (stack under/overflow, type mismatch, div-by-zero, jump OOB)
204 lines
8.5 KiB
C++
204 lines
8.5 KiB
C++
// AVM ISA v1 — C++ Port (Strict Functional Execution)
|
|
#pragma once
|
|
#include <cstdint>
|
|
#include <vector>
|
|
#include <variant>
|
|
#include <optional>
|
|
#include <stdexcept>
|
|
|
|
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<int32_t>(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<int32_t>(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<int32_t>(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<int32_t, bool>;
|
|
|
|
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<int64_t>(std::get<int32_t>(a.val)) + std::get<int32_t>(b.val))};
|
|
case Prim::SubSatQ0:
|
|
check(a, Ty::Q0_16); check(b, Ty::Q0_16);
|
|
return {Ty::Q0_16, avm_q0_clamp(static_cast<int64_t>(std::get<int32_t>(a.val)) - std::get<int32_t>(b.val))};
|
|
case Prim::AddSatQ16:
|
|
check(a, Ty::Q16_16); check(b, Ty::Q16_16);
|
|
return {Ty::Q16_16, avm_clamp(static_cast<int64_t>(std::get<int32_t>(a.val)) + std::get<int32_t>(b.val))};
|
|
case Prim::SubSatQ16:
|
|
check(a, Ty::Q16_16); check(b, Ty::Q16_16);
|
|
return {Ty::Q16_16, avm_clamp(static_cast<int64_t>(std::get<int32_t>(a.val)) - std::get<int32_t>(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<int64_t>(std::get<int32_t>(a.val)) * std::get<int32_t>(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<int64_t>(std::get<int32_t>(a.val)) * Q16_SCALE, std::get<int32_t>(b.val)))};
|
|
case Prim::LtQ16:
|
|
check(a, Ty::Q16_16); check(b, Ty::Q16_16);
|
|
return {Ty::Bool, lt_q16_v6(std::get<int32_t>(a.val), std::get<int32_t>(b.val))};
|
|
case Prim::EqQ16:
|
|
check(a, Ty::Q16_16); check(b, Ty::Q16_16);
|
|
return {Ty::Bool, std::get<int32_t>(a.val) == std::get<int32_t>(b.val)};
|
|
case Prim::And:
|
|
check(a, Ty::Bool); check(b, Ty::Bool);
|
|
return {Ty::Bool, std::get<bool>(a.val) && std::get<bool>(b.val)};
|
|
case Prim::Or:
|
|
check(a, Ty::Bool); check(b, Ty::Bool);
|
|
return {Ty::Bool, std::get<bool>(a.val) || std::get<bool>(b.val)};
|
|
case Prim::Not:
|
|
check(a, Ty::Bool);
|
|
return {Ty::Bool, !std::get<bool>(a.val)};
|
|
}
|
|
throw std::runtime_error("unknown prim");
|
|
}
|
|
|
|
// ── State ─────────────────────────────────────────────────────
|
|
struct State {
|
|
int pc = 0;
|
|
std::vector<AnyVal> stack;
|
|
std::vector<std::optional<AnyVal>> locals;
|
|
bool halted = false;
|
|
};
|
|
|
|
inline State init_state(size_t n_locals = 0) {
|
|
return {0, {}, std::vector<std::optional<AnyVal>>(n_locals), false};
|
|
}
|
|
|
|
// ── Step ─────────────────────────────────────────────────────
|
|
inline std::optional<State> step(const State& s, const std::vector<Instr>& prog) {
|
|
if (s.halted) return std::nullopt;
|
|
if (s.pc < 0 || static_cast<size_t>(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<size_t>(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<bool>(v.val)) {
|
|
if (instr.arg < 0 || static_cast<size_t>(instr.arg) >= prog.size()) return std::nullopt;
|
|
npc = instr.arg;
|
|
}
|
|
break;
|
|
}
|
|
case Op::Primitive: {
|
|
auto p = static_cast<Prim>(instr.arg);
|
|
int arity = prim_arity(p);
|
|
if (static_cast<int>(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<State> run(const State& init, const std::vector<Instr>& 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
|