mirror of
https://github.com/allaunthefox/SilverSight.git
synced 2026-07-31 01:25:21 +00:00
Rust fixes: - Remove Q0_16 Not arm (was silently returning Bool(false) instead of type error) - Replace inline floor division with floor_div() calls in MulSatQ16/DivSatQ16 - Fix comment ranges for Q0_16 [-32767, 32767] and Q16_16 [-2147483647, 2147483647] Go fixes: - Add euclideanDiv() matching Lean Int.ediv (remainder ≥ 0) - Use euclideanDiv for MulSatQ16 and DivSatQ16 - Change Locals from []*Val to []Val (dangling pointer fix) - Step on halted state returns &s, nil (Lean returns Ok s) - OOB PC returns error instead of silent halt - Halt does not increment PC (Lean: PC unchanged) All Go tests pass (9/9)
238 lines
7.9 KiB
Go
238 lines
7.9 KiB
Go
// AVM ISA v1 — Go Port (Strict Functional Execution)
|
|
package avm
|
|
|
|
import "errors"
|
|
|
|
// ── Constants ────────────────────────────────────────────────────
|
|
const (
|
|
AVMClampMin = -2147483647
|
|
AVMClampMax = 2147483647
|
|
AVMQ0Min = -32767
|
|
AVMQ0Max = 32767
|
|
Q16Scale = 65536
|
|
AVMMaxStack = 1024
|
|
)
|
|
|
|
func avmClamp(x int64) int32 {
|
|
if x > AVMClampMax { return AVMClampMax }
|
|
if x < AVMClampMin { return AVMClampMin }
|
|
return int32(x)
|
|
}
|
|
|
|
func avmQ0Clamp(x int64) int32 {
|
|
if x > AVMQ0Max { return AVMQ0Max }
|
|
if x < AVMQ0Min { return AVMQ0Min }
|
|
return int32(x)
|
|
}
|
|
|
|
func floorDiv(a, b int64) int64 {
|
|
if b == 0 { return 0 }
|
|
q := a / b
|
|
r := a % b
|
|
if r != 0 && ((a ^ b) < 0) { q-- }
|
|
return q
|
|
}
|
|
|
|
// euclideanDiv matches Lean Int.ediv (remainder >= 0).
|
|
func euclideanDiv(a, b int64) int64 {
|
|
if b == 0 { return 0 }
|
|
q := a / b
|
|
r := a % b
|
|
if r < 0 {
|
|
if b > 0 { q-- } else { q++ }
|
|
}
|
|
return q
|
|
}
|
|
|
|
func ltQ16V6(a, b int32) bool {
|
|
sa, sb := a < 0, b < 0
|
|
if sa != sb { return sa }
|
|
return a < b
|
|
}
|
|
|
|
// ── Types ──────────────────────────────────────────────────────
|
|
type Ty uint8
|
|
const (
|
|
Q0_16 Ty = 0
|
|
Q16_16 Ty = 1
|
|
Bool Ty = 2
|
|
)
|
|
|
|
type Val struct {
|
|
Ty Ty
|
|
Q int32
|
|
Bval bool
|
|
}
|
|
|
|
// ── Primitives ────────────────────────────────────────────────
|
|
type Prim uint8
|
|
const (
|
|
AddSatQ0 Prim = iota; SubSatQ0; AddSatQ16; SubSatQ16
|
|
MulSatQ16; DivSatQ16; LtQ16; EqQ16
|
|
And; Or; Not
|
|
)
|
|
|
|
func primArity(p Prim) int {
|
|
if p == Not { return 1 }
|
|
return 2
|
|
}
|
|
|
|
// ── Instructions ──────────────────────────────────────────────
|
|
type Op uint8
|
|
const (
|
|
PushQ16 Op = iota; PushBool; PushQ0; Pop; Dup; Swap
|
|
Load; Store; Jump; JumpIf; Primitive; Halt
|
|
)
|
|
|
|
type Instr struct {
|
|
Op Op
|
|
Arg int32
|
|
Arg2 bool
|
|
}
|
|
|
|
// ── Primitive execution ──────────────────────────────────────
|
|
func execPrim(p Prim, a, b Val) (Val, error) {
|
|
check := func(v Val, t Ty) error {
|
|
if v.Ty != t { return errors.New("type mismatch") }
|
|
return nil
|
|
}
|
|
switch p {
|
|
case AddSatQ0:
|
|
if err := check(a, Q0_16); err != nil { return Val{}, err }
|
|
if err := check(b, Q0_16); err != nil { return Val{}, err }
|
|
return Val{Ty: Q0_16, Q: avmQ0Clamp(int64(a.Q) + int64(b.Q))}, nil
|
|
case SubSatQ0:
|
|
if err := check(a, Q0_16); err != nil { return Val{}, err }
|
|
if err := check(b, Q0_16); err != nil { return Val{}, err }
|
|
return Val{Ty: Q0_16, Q: avmQ0Clamp(int64(a.Q) - int64(b.Q))}, nil
|
|
case AddSatQ16:
|
|
if err := check(a, Q16_16); err != nil { return Val{}, err }
|
|
if err := check(b, Q16_16); err != nil { return Val{}, err }
|
|
return Val{Ty: Q16_16, Q: avmClamp(int64(a.Q) + int64(b.Q))}, nil
|
|
case SubSatQ16:
|
|
if err := check(a, Q16_16); err != nil { return Val{}, err }
|
|
if err := check(b, Q16_16); err != nil { return Val{}, err }
|
|
return Val{Ty: Q16_16, Q: avmClamp(int64(a.Q) - int64(b.Q))}, nil
|
|
case MulSatQ16:
|
|
if err := check(a, Q16_16); err != nil { return Val{}, err }
|
|
if err := check(b, Q16_16); err != nil { return Val{}, err }
|
|
return Val{Ty: Q16_16, Q: avmClamp(euclideanDiv(int64(a.Q)*int64(b.Q), Q16Scale))}, nil
|
|
case DivSatQ16:
|
|
if err := check(a, Q16_16); err != nil { return Val{}, err }
|
|
if err := check(b, Q16_16); err != nil { return Val{}, err }
|
|
if b.Q == 0 { return Val{}, errors.New("division by zero") }
|
|
return Val{Ty: Q16_16, Q: avmClamp(euclideanDiv(int64(a.Q)*Q16Scale, int64(b.Q)))}, nil
|
|
case LtQ16:
|
|
if err := check(a, Q16_16); err != nil { return Val{}, err }
|
|
if err := check(b, Q16_16); err != nil { return Val{}, err }
|
|
return Val{Ty: Bool, Bval: ltQ16V6(a.Q, b.Q)}, nil
|
|
case EqQ16:
|
|
if err := check(a, Q16_16); err != nil { return Val{}, err }
|
|
if err := check(b, Q16_16); err != nil { return Val{}, err }
|
|
return Val{Ty: Bool, Bval: a.Q == b.Q}, nil
|
|
case And:
|
|
if err := check(a, Bool); err != nil { return Val{}, err }
|
|
if err := check(b, Bool); err != nil { return Val{}, err }
|
|
return Val{Ty: Bool, Bval: a.Bval && b.Bval}, nil
|
|
case Or:
|
|
if err := check(a, Bool); err != nil { return Val{}, err }
|
|
if err := check(b, Bool); err != nil { return Val{}, err }
|
|
return Val{Ty: Bool, Bval: a.Bval || b.Bval}, nil
|
|
case Not:
|
|
if err := check(a, Bool); err != nil { return Val{}, err }
|
|
return Val{Ty: Bool, Bval: !a.Bval}, nil
|
|
}
|
|
return Val{}, errors.New("unknown prim")
|
|
}
|
|
|
|
// ── State ─────────────────────────────────────────────────────
|
|
type State struct {
|
|
Pc int
|
|
Stack []Val
|
|
Locals []Val
|
|
Halted bool
|
|
}
|
|
|
|
func NewState(nLocals int) State {
|
|
return State{Pc: 0, Stack: []Val{}, Locals: make([]Val, nLocals), Halted: false}
|
|
}
|
|
|
|
// ── Step ─────────────────────────────────────────────────────
|
|
func Step(s State, prog []Instr) (*State, error) {
|
|
if s.Halted { return &s, nil }
|
|
if s.Pc < 0 || s.Pc >= len(prog) {
|
|
return nil, errors.New("invalid jump")
|
|
}
|
|
|
|
instr := prog[s.Pc]
|
|
stack := s.Stack
|
|
npc := s.Pc + 1
|
|
|
|
growing := instr.Op == PushQ16 || instr.Op == PushBool || instr.Op == PushQ0 || instr.Op == Dup || instr.Op == Load
|
|
if growing && len(stack) >= AVMMaxStack { return nil, errors.New("stack overflow") }
|
|
|
|
switch instr.Op {
|
|
case PushQ16:
|
|
stack = append(stack, Val{Ty: Q16_16, Q: avmClamp(int64(instr.Arg))})
|
|
case PushBool:
|
|
stack = append(stack, Val{Ty: Bool, Bval: instr.Arg2})
|
|
case PushQ0:
|
|
stack = append(stack, Val{Ty: Q0_16, Q: avmQ0Clamp(int64(instr.Arg))})
|
|
case Pop:
|
|
if len(stack) == 0 { return nil, errors.New("empty stack") }
|
|
stack = stack[:len(stack)-1]
|
|
case Dup:
|
|
if len(stack) == 0 { return nil, errors.New("empty stack") }
|
|
stack = append(stack, stack[len(stack)-1])
|
|
case Swap:
|
|
if len(stack) < 2 { return nil, errors.New("stack underflow") }
|
|
stack[len(stack)-1], stack[len(stack)-2] = stack[len(stack)-2], stack[len(stack)-1]
|
|
case Load:
|
|
i := int(instr.Arg)
|
|
if i >= len(s.Locals) { return nil, errors.New("missing local") }
|
|
stack = append(stack, s.Locals[i])
|
|
case Store:
|
|
if len(stack) == 0 { return nil, errors.New("empty stack") }
|
|
i := int(instr.Arg)
|
|
if i >= len(s.Locals) { return nil, errors.New("missing local") }
|
|
s.Locals[i] = stack[len(stack)-1]
|
|
stack = stack[:len(stack)-1]
|
|
case Jump:
|
|
if instr.Arg < 0 || int(instr.Arg) >= len(prog) { return nil, errors.New("jump OOB") }
|
|
npc = int(instr.Arg)
|
|
case JumpIf:
|
|
if len(stack) == 0 { return nil, errors.New("empty stack") }
|
|
v := stack[len(stack)-1]; stack = stack[:len(stack)-1]
|
|
if v.Ty != Bool { return nil, errors.New("type mismatch") }
|
|
if v.Bval {
|
|
if instr.Arg < 0 || int(instr.Arg) >= len(prog) { return nil, errors.New("jump OOB") }
|
|
npc = int(instr.Arg)
|
|
}
|
|
case Primitive:
|
|
p := Prim(instr.Arg)
|
|
arity := primArity(p)
|
|
if len(stack) < arity { return nil, errors.New("stack underflow") }
|
|
var b Val
|
|
if arity >= 2 { b = stack[len(stack)-1]; stack = stack[:len(stack)-1] }
|
|
a := stack[len(stack)-1]; stack = stack[:len(stack)-1]
|
|
r, execErr := execPrim(p, a, b)
|
|
if execErr != nil { return nil, execErr }
|
|
stack = append(stack, r)
|
|
case Halt:
|
|
s.Halted = true
|
|
npc = s.Pc // Lean: Halt does not increment PC
|
|
}
|
|
return &State{Pc: npc, Stack: stack, Locals: s.Locals, Halted: s.Halted}, nil
|
|
}
|
|
|
|
// ── Run (fuel-bounded) ───────────────────────────────────────
|
|
func Run(init State, prog []Instr, fuel int) (*State, error) {
|
|
s := init
|
|
for i := 0; i < fuel; i++ {
|
|
if s.Halted { return &s, nil }
|
|
next, err := Step(s, prog)
|
|
if err != nil { return nil, err }
|
|
s = *next
|
|
}
|
|
return &s, nil
|
|
}
|