use std::collections::HashMap; use std::sync::Arc; use crate::asm::data::literal::{Addr, Label, RoutineName}; use crate::asm::instr::Op; use crate::runtime::fault::Fault; #[derive(Debug)] pub struct Program { pub ops: Vec, routines: HashMap, far_labels: HashMap, barriers: Vec, } impl Program { pub fn new(ops: Vec) -> Arc { let mut p = Self { ops, routines: Default::default(), far_labels: Default::default(), barriers: Default::default(), }; p.scan(); Arc::new(p) } /// Find all the named things fn scan(&mut self) { for (pos, op) in self.ops.iter().enumerate() { match op { Op::FarLabel(name) => { self.far_labels.insert(name.clone(), pos.into()); } Op::Routine(name) => { self.routines.insert(name.clone(), pos.into()); } Op::Barrier(_) => { self.barriers.push(pos.into()); } _ => {} } } } /// Read a program instruction at address pub fn read(&self, addr: Addr) -> &Op { if addr.0 >= self.ops.len() as u64 { &Op::Nop } else { &self.ops[addr.0 as usize] } } /// Return error if any barrier is crossed during a jump / skip pub fn validate_jump(&self, mut from: Addr, mut to: Addr) -> Result<(), Fault> { if from > to { std::mem::swap(&mut from, &mut to); } for b in &self.barriers { if *b >= from && *b <= to { if let Op::Barrier(msg) = self.read(*b) { return Err(Fault::JumpThroughBarrier { msg: msg.clone().unwrap_or("No msg".into()) }); } else { unreachable!(); } } } Ok(()) } /// Find routine by name pub fn find_routine(&self, name: &RoutineName) -> Result { self.routines.get(name).copied() .ok_or_else(|| Fault::NoSuchRoutine { routine: name.clone() }) } /// Find far label by name pub fn find_far_label(&self, name: &Label) -> Result { self.far_labels.get(name).copied() .ok_or_else(|| Fault::NoSuchLabel { label: name.clone() }) } /// Find local label by name (label within a boundary-delimited region). /// If more than one such label exists, the outcome is undefined. pub fn find_local_label(&self, pc: Addr, name: &Label) -> Result { // TODO more efficient impl with look-up for at in (0..=pc.0).rev() { match &self.ops[at as usize] { Op::Label(lbl) if lbl == name => { return Ok(at.into()); } Op::Barrier(_) => { break; } _ => {} } } for at in pc.0..(self.ops.len() as u64) { match &self.ops[at as usize] { Op::Label(lbl) if lbl == name => { return Ok(at.into()); } Op::Barrier(_) => { break; } _ => {} } } Err(Fault::NoSuchLabel { label: name.clone() }) } }