diff --git a/interp/src/debugger/debugging_context/context.rs b/interp/src/debugger/debugging_context/context.rs index b1857b2fd9..fad5719e56 100644 --- a/interp/src/debugger/debugging_context/context.rs +++ b/interp/src/debugger/debugging_context/context.rs @@ -58,17 +58,7 @@ impl BreakPoint { } pub fn format(&self, ctx: &Context) -> String { - let parent_comp = ctx - .primary - .components - .keys() - .find(|comp_id| { - ctx.secondary[*comp_id] - .definitions - .groups() - .contains(self.group) - }) - .unwrap(); + let parent_comp = ctx.get_component_from_group(self.group); let parent_name = ctx.lookup_name(parent_comp); let group_name = ctx.lookup_name(self.group); diff --git a/interp/src/flatten/flat_ir/component.rs b/interp/src/flatten/flat_ir/component.rs index 4211d286b2..8606dad029 100644 --- a/interp/src/flatten/flat_ir/component.rs +++ b/interp/src/flatten/flat_ir/component.rs @@ -1,5 +1,7 @@ use crate::flatten::structures::{ - index_trait::IndexRange, indexed_map::IndexedMap, sparse_map::SparseMap, + index_trait::{IndexRange, SignatureRange}, + indexed_map::IndexedMap, + sparse_map::SparseMap, }; use super::{control::structures::ControlIdx, prelude::*}; @@ -75,8 +77,12 @@ pub struct ComponentCore { pub struct AuxillaryComponentInfo { /// Name of the component. pub name: Identifier, - /// The input/output signature of this component. - pub signature: IndexRange, + /// The input/output signature of this component. This could probably be + /// rolled into a single range, or specialized construct but this is + /// probably fine for now. + pub signature_in: SignatureRange, + pub signature_out: SignatureRange, + /// the definitions created by this component pub definitions: DefinitionRanges, @@ -100,7 +106,8 @@ impl AuxillaryComponentInfo { pub fn new_with_name(id: Identifier) -> Self { Self { name: id, - signature: IndexRange::empty_interval(), + signature_in: SignatureRange::new(), + signature_out: SignatureRange::new(), port_offset_map: Default::default(), ref_port_offset_map: Default::default(), cell_offset_map: Default::default(), @@ -153,15 +160,44 @@ impl AuxillaryComponentInfo { self.definitions.comb_groups = IndexRange::new(start, end) } + pub fn inputs(&self) -> impl Iterator + '_ { + self.signature_in.iter() + } + + pub fn outputs(&self) -> impl Iterator + '_ { + self.signature_out.iter() + } + + pub fn signature(&self) -> IndexRange { + // can't quite use min here since None is less than any other value and + // I want the least non-None value + let beginning = + match (self.signature_in.first(), self.signature_out.first()) { + (Some(b), Some(e)) => Some(std::cmp::min(b, e)), + (Some(b), None) => Some(b), + (None, Some(e)) => Some(e), + _ => None, + }; + + let end = + std::cmp::max(self.signature_in.last(), self.signature_out.last()); + + match (beginning, end) { + (Some(b), Some(e)) => IndexRange::new(b, e), + (None, None) => IndexRange::empty_interval(), + _ => unreachable!(), + } + } + fn offset_sizes(&self, cell_ty: ContainmentType) -> IdxSkipSizes { let (port, ref_port) = match cell_ty { ContainmentType::Local => ( - self.port_offset_map.count() - self.signature.size(), + self.port_offset_map.count() - self.signature().size(), self.ref_port_offset_map.count(), ), ContainmentType::Ref => ( self.port_offset_map.count(), - self.ref_port_offset_map.count() - self.signature.size(), + self.ref_port_offset_map.count() - self.signature().size(), ), }; diff --git a/interp/src/flatten/flat_ir/control/translator.rs b/interp/src/flatten/flat_ir/control/translator.rs index f947a04aa8..b7070dcfc0 100644 --- a/interp/src/flatten/flat_ir/control/translator.rs +++ b/interp/src/flatten/flat_ir/control/translator.rs @@ -17,7 +17,7 @@ use crate::{ }, structures::{ context::{Context, InterpretationContext, SecondaryContext}, - index_trait::IndexRange, + index_trait::{IndexRange, SignatureRange}, }, }, }; @@ -369,19 +369,29 @@ fn compute_local_layout( let mut layout = Layout::default(); - // need this to set the appropriate signature range on the component - let sig_base = aux.port_offset_map.peek_next_index(); + let mut sigs_input = SignatureRange::new(); + let mut sigs_output = SignatureRange::new(); - // first, handle the signature ports - for port in comp.signature.borrow().ports() { + // first, handle the input signature ports + for port in comp.signature.borrow().ports().into_iter() { let local_offset = insert_port(&mut ctx.secondary, aux, port, ContainmentType::Local); + match &port.borrow().direction { + cir::Direction::Input => { + sigs_output.append_item(*local_offset.as_local().unwrap()); + } + cir::Direction::Output => { + sigs_input.append_item(*local_offset.as_local().unwrap()); + } + _ => unreachable!("inout port in component signature"), + } + layout.port_map.insert(port.as_raw(), local_offset); } // update the aux info with the signature layout - aux.signature = - IndexRange::new(sig_base, aux.port_offset_map.peek_next_index()); + aux.signature_in = sigs_input; + aux.signature_out = sigs_output; // second the group holes for group in &comp.groups { diff --git a/interp/src/flatten/structures/context.rs b/interp/src/flatten/structures/context.rs index e0c190f938..b384995afc 100644 --- a/interp/src/flatten/structures/context.rs +++ b/interp/src/flatten/structures/context.rs @@ -342,6 +342,19 @@ impl Context { .find(|x| self.resolve_id(self.primary[*x].name()) == name) } + pub fn get_component_from_group(&self, group: GroupIdx) -> ComponentIdx { + self.primary + .components + .keys() + .find(|comp_id| { + self.secondary[*comp_id] + .definitions + .groups() + .contains(group) + }) + .unwrap() + } + /// This is a wildly inefficient search, only used for debugging right now. /// TODO Griffin: if relevant, replace with something more efficient. pub(crate) fn find_parent_cell( @@ -351,7 +364,7 @@ impl Context { ) -> ParentIdx { match target { PortRef::Local(l) => { - if self.secondary[comp].signature.contains(l) { + if self.secondary[comp].signature().contains(l) { comp.into() } else { //I would not recommend looking at this code diff --git a/interp/src/flatten/structures/environment/env.rs b/interp/src/flatten/structures/environment/env.rs index 7599726457..ee878317c3 100644 --- a/interp/src/flatten/structures/environment/env.rs +++ b/interp/src/flatten/structures/environment/env.rs @@ -224,6 +224,32 @@ impl Debug for CellLedger { } } +#[derive(Debug, Clone)] +struct PinnedPorts { + port_vals_list: Vec<(GlobalPortIdx, Value)>, +} + +impl PinnedPorts { + pub fn iter(&self) -> impl Iterator + '_ { + self.port_vals_list.iter() + } + + pub fn new() -> Self { + Self { + port_vals_list: vec![], + } + } + + pub fn push(&mut self, port: GlobalPortIdx, val: Value) { + // linear scan is probably fine here since the list should be relatively small + assert!( + !self.port_vals_list.iter().any(|x| x.0 == port), + "attempting to pin the same port twice" + ); + self.port_vals_list.push((port, val)); + } +} + #[derive(Debug)] pub struct Environment + Clone> { /// A map from global port IDs to their current values. @@ -238,6 +264,8 @@ pub struct Environment + Clone> { /// The program counter for the whole program execution. pub(super) pc: ProgramCounter, + pinned_ports: PinnedPorts, + /// The immutable context. This is retained for ease of use. /// This value should have a cheap clone implementation, such as &Context /// or RC. @@ -272,7 +300,7 @@ impl + Clone> Environment { let comp_ledger = self.cells[cell].as_comp().unwrap(); let comp_info = self.ctx().secondary.comp_aux_info.get(comp_ledger.comp_id); - let port_ids = comp_info.signature.into_iter().map(|x| { + let port_ids = comp_info.signature().into_iter().map(|x| { &self.ctx().secondary.local_port_defs [comp_info.port_offset_map[x]] .name @@ -324,6 +352,7 @@ impl + Clone> Environment { pc: ProgramCounter::new_empty(), ctx, memory_header: None, + pinned_ports: PinnedPorts::new(), }; let root_node = CellLedger::new_comp(root, &env); @@ -386,7 +415,7 @@ impl + Clone> Environment { } // first layout the signature - for sig_port in comp_aux.signature.iter() { + for sig_port in comp_aux.signature().iter() { let idx = self.ports.push(PortValue::new_undef()); debug_assert_eq!(index_bases + sig_port, idx); } @@ -860,7 +889,7 @@ impl + Clone> Environment { // signature ports if let Some(local) = target.as_port() { let offset = local - ¤t_ledger.index_bases; - if current_info.signature.contains(offset) { + if current_info.signature().contains(offset) { return Some((path, None)); } } @@ -1020,7 +1049,7 @@ impl + Clone> Environment { } else { let ledger = self.cells[cell].as_comp().unwrap(); let comp = &self.ctx.as_ref().secondary[ledger.comp_id]; - Box::new(comp.signature.iter().map(|x| { + Box::new(comp.signature().into_iter().map(|x| { let def_idx = comp.port_offset_map[x]; let def = &self.ctx.as_ref().secondary[def_idx]; (def.name, &ledger.index_bases + x) @@ -1047,6 +1076,43 @@ impl + Clone> Environment { }, ) } + + /// Lookup the value of a port on the entrypoint component by name. Will + /// error if the port is not found. + pub fn lookup_port_from_string(&self, port: &String) -> Option { + // this is not the best way to do this but it's fine for now + let path = self.traverse_name_vec(&[port.to_string()]).unwrap(); + let path_resolution = path.resolve_path(self).unwrap(); + let idx = path_resolution.as_port().unwrap(); + + self.ports[*idx].as_option().map(|x| x.val().clone()) + } + + /// Pins the port with the given name to the given value. This may only be + /// used for input ports on the entrypoint component (excluding the go port) + /// and will panic if used otherwise. Intended for external use. Unrelated + /// to the rust pin. + pub fn pin_value>(&mut self, port: S, val: Value) { + let string = port.as_ref(); + + let root = Self::get_root(); + + let ledger = self.cells[root].as_comp().unwrap(); + let mut def_list = self.ctx.as_ref().secondary[ledger.comp_id].inputs(); + let found = def_list.find(|offset| { + let def_idx = self.ctx.as_ref().secondary[ledger.comp_id].port_offset_map[*offset]; + self.ctx.as_ref().lookup_name(self.ctx.as_ref().secondary[def_idx].name) == string + }).expect("Could not find port with given name in the entrypoint component's input ports"); + + assert!( + found != self.ctx.as_ref().primary[ledger.comp_id].go, + "Cannot pin the go port" + ); + + let found = &ledger.index_bases + found; + + self.pinned_ports.push(found, val); + } } /// A wrapper struct for the environment that provides the functions used to @@ -1129,6 +1195,19 @@ impl + Clone> Simulator { pub fn print_pc(&self) { self.env.print_pc() } + + /// Pins the port with the given name to the given value. This may only be + /// used for input ports on the entrypoint component (excluding the go port) + /// and will panic if used otherwise. Intended for external use. + pub fn pin_value>(&mut self, port: S, val: Value) { + self.env.pin_value(port, val) + } + + /// Lookup the value of a port on the entrypoint component by name. Will + /// error if the port is not found. + pub fn lookup_port_from_string(&self, port: &String) -> Option { + self.env.lookup_port_from_string(port) + } } // =========================== simulation functions =========================== @@ -1364,6 +1443,10 @@ impl + Clone> Simulator { pub fn converge(&mut self) -> InterpreterResult<()> { self.undef_all_ports(); self.set_root_go_high(); + // set the pinned values + for (port, val) in self.env.pinned_ports.iter() { + self.env.ports[*port] = PortValue::new_implicit(val.clone()); + } for comp in self.env.pc.finished_comps() { let done_port = self.env.get_comp_done(*comp); diff --git a/interp/src/flatten/structures/index_trait.rs b/interp/src/flatten/structures/index_trait.rs index deb3a3bc2c..b816bad239 100644 --- a/interp/src/flatten/structures/index_trait.rs +++ b/interp/src/flatten/structures/index_trait.rs @@ -91,6 +91,10 @@ macro_rules! impl_index_nonzero { }; } +use smallvec::{smallvec, SmallVec}; + +use crate::flatten::flat_ir::base::LocalPortOffset; + pub(crate) use {impl_index, impl_index_nonzero}; /// A half open range of indices. The start is inclusive, the end is exclusive. @@ -150,6 +154,27 @@ where pub fn set_end(&mut self, end: I) { self.end = end; } + + pub fn start(&self) -> I { + self.start + } + + pub fn end(&self) -> I { + self.end + } +} + +impl IntoIterator for IndexRange +where + I: IndexRef + PartialOrd, +{ + type Item = I; + + type IntoIter = OwnedIndexRangeIterator; + + fn into_iter(self) -> Self::IntoIter { + OwnedIndexRangeIterator::new(self) + } } impl<'a, I> IntoIterator for &'a IndexRange @@ -217,3 +242,184 @@ where (size, Some(size)) } } + +/// Because I really played myself by making the [IndexRangeIterator] have a +/// lifetime attached to it. This one doesn't do that. As with it's sibling, the +/// range is half open, meaning that the start is inclusive, but the end is +/// exclusive. +pub struct OwnedIndexRangeIterator +where + I: IndexRef + PartialOrd, +{ + range: IndexRange, + current: I, +} + +impl OwnedIndexRangeIterator +where + I: IndexRef + PartialOrd, +{ + pub fn new(range: IndexRange) -> Self { + Self { + range, + current: range.start, + } + } +} + +impl Iterator for OwnedIndexRangeIterator +where + I: IndexRef + PartialOrd, +{ + type Item = I; + + fn next(&mut self) -> Option { + if self.current < self.range.end { + let current = self.current; + self.current = I::new(self.current.index() + 1); + Some(current) + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option) { + let size = if self.range.end.index() > self.current.index() { + self.range.end.index() - self.current.index() + } else { + 0 + }; + + (size, Some(size)) + } +} + +impl ExactSizeIterator for OwnedIndexRangeIterator where + I: IndexRef + PartialOrd +{ +} + +#[derive(Debug, Clone)] +pub struct ConcatenatedIndexRanges( + SmallVec<[IndexRange; N]>, +) +where + I: IndexRef + PartialOrd; + +impl std::ops::Add> + for ConcatenatedIndexRanges +where + I: IndexRef + PartialOrd, +{ + type Output = Self; + + fn add(mut self, rhs: IndexRange) -> Self::Output { + self.append(rhs); + self + } +} + +impl std::ops::AddAssign> + for ConcatenatedIndexRanges +where + I: IndexRef + PartialOrd, +{ + fn add_assign(&mut self, rhs: IndexRange) { + self.append(rhs); + } +} + +impl std::ops::AddAssign for ConcatenatedIndexRanges +where + I: IndexRef + PartialOrd, +{ + fn add_assign(&mut self, rhs: I) { + self.append_item(rhs); + } +} + +impl ConcatenatedIndexRanges +where + I: IndexRef + PartialOrd, +{ + pub fn new() -> Self { + Self(SmallVec::new()) + } + + /// Appends a range to the concatenated ranges. This requires that the + /// appended range starts after the last range in the concatenation. If it + /// is consecutive with the last range, then the last range will be extended. + pub fn append(&mut self, value: IndexRange) { + if let Some(last) = self.0.last_mut() { + if last.end == value.start { + last.set_end(value.end); + return; + } else { + assert!( + last.end < value.start, + "Ranges must be strictly increasing" + ); + } + } + + self.0.push(value); + } + + pub fn append_item(&mut self, value: I) { + if let Some(last) = self.0.last_mut() { + if last.end == value { + last.set_end(I::new(last.end.index() + 1)); + return; + } else { + assert!(last.end < value, "Inserted value must not be less than end of the prior range"); + } + } + self.0 + .push(IndexRange::new(value, I::new(value.index() + 1))); + } + + pub fn iter(&self) -> impl Iterator + '_ { + self.0.iter().flat_map(|x| x.iter()) + } + + pub fn contains(&self, candidate: I) -> bool { + for range in self.0.iter() { + if range.start > candidate { + return false; + } else if range.end > candidate { + return true; + } else { + continue; + } + } + false + } + + pub fn first(&self) -> Option { + self.0.first().map(|x| x.start) + } + + pub fn last(&self) -> Option { + self.0.last().map(|x| x.end) + } +} + +impl Default for ConcatenatedIndexRanges +where + I: IndexRef + PartialOrd, +{ + fn default() -> Self { + Self::new() + } +} + +impl From> for ConcatenatedIndexRanges +where + I: IndexRef + PartialOrd, +{ + fn from(value: IndexRange) -> Self { + Self(smallvec![value]) + } +} + +pub type SignatureRange = ConcatenatedIndexRanges;