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

119 lines
3.4 KiB

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<Op>,
routines: HashMap<RoutineName, Addr>,
far_labels: HashMap<Label, Addr>,
barriers: Vec<Addr>,
}
impl Program {
pub fn new(ops: Vec<Op>) -> Arc<Self> {
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<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() })
}
/// 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<Addr, Fault> {
// 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() })
}
}