use std::collections::HashMap; use std::sync::Arc; use crate::asm::data::literal::{Addr, Label, RoutineName}; use crate::asm::instr::Op; use crate::asm::instr::op::OpKind; use crate::builtin::defs::{Barrier, BuiltinOp}; use crate::module::CrsnExtension; use crate::runtime::fault::Fault; #[derive(Debug)] pub struct Program { pub ops: Vec, pub extensions: Arc>>, routines: HashMap, far_labels: HashMap, /// Barriers from-to (inclusive). /// Standalone barriers have both addresses the same. barriers: Vec<(Addr, Addr)>, } impl Program { pub fn new(ops: Vec, extensions: Arc>>) -> anyhow::Result> { let mut p = Self { ops, extensions, routines: Default::default(), far_labels: Default::default(), barriers: Default::default(), }; p.scan()?; Ok(Arc::new(p)) } /// Find all the named things fn scan(&mut self) -> anyhow::Result<()> { let mut barrier_starts: HashMap<&Label, Addr> = HashMap::new(); for (pos, op) in self.ops.iter().enumerate() { match &op.kind { OpKind::BuiltIn(BuiltinOp::FarLabel(name)) => { self.far_labels.insert(name.clone(), pos.into()); } OpKind::BuiltIn(BuiltinOp::Routine(name)) => { self.routines.insert(name.clone(), pos.into()); } OpKind::BuiltIn( BuiltinOp::Barrier { kind: Barrier::Open(lbl), .. } ) => { barrier_starts.insert(lbl, pos.into()); } OpKind::BuiltIn( BuiltinOp::Barrier { kind: Barrier::Close(lbl), msg, } ) => { if let Some(start_pos) = barrier_starts.remove(lbl) { self.barriers.push((start_pos, pos.into())); self.far_labels.insert(lbl.clone(), pos.into()); } else { anyhow::bail!("Block barrier \"{:?}\" closed without being open!", msg); } } OpKind::BuiltIn( BuiltinOp::Barrier { kind: Barrier::Standalone, .. } ) => { self.barriers.push((pos.into(), pos.into())); } _ => {} } } if !barrier_starts.is_empty() { anyhow::bail!("Some block barriers open without being closed!"); } trace!("Program scanned: {:?}", self); Ok(()) } /// Read a program instruction at address pub fn read(&self, addr: Addr) -> &Op { if addr.0 >= self.ops.len() as u64 { &Op { kind: OpKind::BuiltIn(BuiltinOp::Halt), cond: None } } 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 (b0, b1) in &self.barriers { if b0 != b1 { // block barrier that only partially intersects the jump if (*b0 >= from && *b0 <= to) != (*b1 >= from && *b1 <= to) { if let OpKind::BuiltIn(BuiltinOp::Barrier { msg, .. }) = &self.read(*b0).kind { return Err(Fault::JumpThroughBarrier { msg: msg.clone().unwrap_or("BLOCK BARRIER".into()) }); } else { unreachable!(); } } } else { // point barrier if *b0 >= from && *b0 <= to { if let OpKind::BuiltIn(BuiltinOp::Barrier { msg, .. }) = &self.read(*b0).kind { return Err(Fault::JumpThroughBarrier { msg: msg.clone().unwrap_or("POINT BARRIER".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() }) } }