javm 0.4.0

Join-Accumulate VM (JAVM).
Documentation
//! Single-pass pipeline gas model (JAR v0.8.0).
//!
//! O(n) single-pass model tracking per-register completion cycles.
//! Replaces the full ROB-based pipeline simulation.
//!
//! Tracks `reg_done[13]` (cycle when each register is ready) and decode
//! throughput (4 slots/cycle). No ROB, no priority loop, no EU contention.
//! See `docs/gas-metering-design.md` for detailed comparison.

use crate::gas_cost::FastCost;

/// Single-pass pipeline gas simulator. O(1) per instruction, stack-allocated.
pub struct GasSimulator {
    reg_done: [u32; 13],
    cycle: u32,
    decode_used: u8,
    max_done: u32,
}

impl GasSimulator {
    pub fn new() -> Self {
        Self {
            reg_done: [0; 13],
            cycle: 0,
            decode_used: 0,
            max_done: 0,
        }
    }

    /// Process one instruction. O(1).
    #[inline]
    pub fn feed(&mut self, cost: &FastCost) {
        // Decode throughput: 4 slots per cycle
        self.decode_used += cost.decode_slots;
        if self.decode_used > 4 {
            self.cycle += 1;
            self.decode_used = cost.decode_slots;
        }

        // move_reg: zero-cycle frontend-only op, propagate reg_done
        if cost.is_move_reg {
            let src_reg = cost.src_mask.trailing_zeros() as usize;
            let dst_reg = cost.dst_mask.trailing_zeros() as usize;
            if src_reg < 13 && dst_reg < 13 {
                self.reg_done[dst_reg] = self.reg_done[src_reg];
            }
            return;
        }

        // Data dependencies: start = max(decode_cycle, max(reg_done[src_regs]))
        let mut start = self.cycle;
        let mut src = cost.src_mask;
        while src != 0 {
            let r = src.trailing_zeros() as usize;
            src &= src - 1;
            if r < 13 {
                start = start.max(self.reg_done[r]);
            }
        }

        // Completion
        let done = start + cost.cycles as u32;

        // Update destination registers
        let mut dst = cost.dst_mask;
        while dst != 0 {
            let r = dst.trailing_zeros() as usize;
            dst &= dst - 1;
            if r < 13 {
                self.reg_done[r] = done;
            }
        }

        // Track maximum completion cycle
        self.max_done = self.max_done.max(done);
    }

    /// Return block gas cost: max(max_done - 3, 1).
    #[inline]
    pub fn flush_and_get_cost(&self) -> u32 {
        if self.max_done > 3 { self.max_done - 3 } else { 1 }
    }

    /// Reset for the next gas block.
    #[inline]
    pub fn reset(&mut self) {
        self.reg_done = [0; 13];
        self.cycle = 0;
        self.decode_used = 0;
        self.max_done = 0;
    }
}