|
|
|
@ -3,7 +3,7 @@ use std::sync::Arc; |
|
|
|
|
|
|
|
|
|
use crate::asm::data::literal::{Addr, Label, RoutineName}; |
|
|
|
|
use crate::asm::instr::Op; |
|
|
|
|
use crate::builtin::defs::BuiltinOp; |
|
|
|
|
use crate::builtin::defs::{BuiltinOp, Barrier}; |
|
|
|
|
use crate::module::CrsnExtension; |
|
|
|
|
use crate::runtime::fault::Fault; |
|
|
|
|
|
|
|
|
@ -12,25 +12,30 @@ pub struct Program { |
|
|
|
|
pub ops: Vec<Op>, |
|
|
|
|
pub extensions: Arc<Vec<Box<dyn CrsnExtension>>>, |
|
|
|
|
routines: HashMap<RoutineName, Addr>, |
|
|
|
|
local_labels: HashMap<Label, Addr>, |
|
|
|
|
far_labels: HashMap<Label, Addr>, |
|
|
|
|
barriers: Vec<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>>>) -> Arc<Self> { |
|
|
|
|
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(), |
|
|
|
|
local_labels: Default::default(), |
|
|
|
|
far_labels: Default::default(), |
|
|
|
|
barriers: Default::default(), |
|
|
|
|
}; |
|
|
|
|
p.scan(); |
|
|
|
|
Arc::new(p) |
|
|
|
|
p.scan()?; |
|
|
|
|
Ok(Arc::new(p)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Find all the named things
|
|
|
|
|
fn scan(&mut self) { |
|
|
|
|
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 { |
|
|
|
|
Op::BuiltIn(BuiltinOp::FarLabel(name)) => { |
|
|
|
@ -39,12 +44,45 @@ impl Program { |
|
|
|
|
Op::BuiltIn(BuiltinOp::Routine(name)) => { |
|
|
|
|
self.routines.insert(name.clone(), pos.into()); |
|
|
|
|
} |
|
|
|
|
Op::BuiltIn(BuiltinOp::Barrier(_)) => { |
|
|
|
|
self.barriers.push(pos.into()); |
|
|
|
|
Op::BuiltIn( |
|
|
|
|
BuiltinOp::Barrier { |
|
|
|
|
kind: Barrier::Open(lbl), .. |
|
|
|
|
} |
|
|
|
|
) => { |
|
|
|
|
barrier_starts.insert(lbl, pos.into()); |
|
|
|
|
} |
|
|
|
|
Op::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); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
Op::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!"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
debug!("Program scanned: {:?}", self); |
|
|
|
|
|
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Read a program instruction at address
|
|
|
|
@ -62,14 +100,28 @@ impl Program { |
|
|
|
|
std::mem::swap(&mut from, &mut to); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for b in &self.barriers { |
|
|
|
|
if *b >= from && *b <= to { |
|
|
|
|
if let Op::BuiltIn(BuiltinOp::Barrier(msg)) = self.read(*b) { |
|
|
|
|
return Err(Fault::JumpThroughBarrier { |
|
|
|
|
msg: msg.clone().unwrap_or("No msg".into()) |
|
|
|
|
}); |
|
|
|
|
} else { |
|
|
|
|
unreachable!(); |
|
|
|
|
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 Op::BuiltIn(BuiltinOp::Barrier { msg, .. }) = self.read(*b0) { |
|
|
|
|
return Err(Fault::JumpThroughBarrier { |
|
|
|
|
msg: msg.clone().unwrap_or("BLOCK BARRIER".into()) |
|
|
|
|
}); |
|
|
|
|
} else { |
|
|
|
|
unreachable!(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
// point barrier
|
|
|
|
|
if *b0 >= from && *b0 <= to { |
|
|
|
|
if let Op::BuiltIn(BuiltinOp::Barrier { msg, .. }) = self.read(*b0) { |
|
|
|
|
return Err(Fault::JumpThroughBarrier { |
|
|
|
|
msg: msg.clone().unwrap_or("POINT BARRIER".into()) |
|
|
|
|
}); |
|
|
|
|
} else { |
|
|
|
|
unreachable!(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -87,37 +139,5 @@ impl Program { |
|
|
|
|
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::BuiltIn(BuiltinOp::Label(lbl)) if lbl == name => { |
|
|
|
|
return Ok(at.into()); |
|
|
|
|
} |
|
|
|
|
Op::BuiltIn(BuiltinOp::Barrier(_)) => { |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
_ => {} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for at in pc.0..(self.ops.len() as u64) { |
|
|
|
|
match &self.ops[at as usize] { |
|
|
|
|
Op::BuiltIn(BuiltinOp::Label(lbl)) if lbl == name => { |
|
|
|
|
return Ok(at.into()); |
|
|
|
|
} |
|
|
|
|
Op::BuiltIn(BuiltinOp::Barrier(_)) => { |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
_ => {} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Err(Fault::NoSuchLabel { label: name.clone() }) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|