|
|
|
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() })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|