Croissant Runtime
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
crsn/crsn/src/runtime/program.rs

142 lines
4.9 KiB

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<Op>,
pub extensions: Arc<Vec<Box<dyn CrsnExtension>>>,
routines: HashMap<RoutineName, Addr>,
far_labels: HashMap<Label, Addr>,
/// Barriers from-to (inclusive).
/// Standalone barriers have both addresses the same.
barriers: Vec<(Addr, Addr)>,
}
impl Program {
pub fn new(ops: Vec<Op>, extensions: Arc<Vec<Box<dyn CrsnExtension>>>) -> anyhow::Result<Arc<Self>> {
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<Addr, Fault> {
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<Addr, Fault> {
self.far_labels.get(name).copied()
.ok_or_else(|| Fault::NoSuchLabel { label: name.clone() })
}
}