From 7614bea63b59d60667d27fd5e7a976f03a35df3a Mon Sep 17 00:00:00 2001 From: Griffin Berlstein Date: Thu, 22 Aug 2024 14:33:36 -0400 Subject: [PATCH] [Cider] Documentation pass (#2268) Minor refactoring a documentation pass --- interp/src/configuration.rs | 25 +- .../src/debugger/commands/command_parser.rs | 1 + interp/src/debugger/commands/commands.pest | 22 +- interp/src/debugger/commands/core.rs | 151 ++++++++---- interp/src/debugger/commands/mod.rs | 1 + interp/src/debugger/debugger_core.rs | 17 +- .../src/debugger/debugging_context/context.rs | 14 ++ interp/src/debugger/source/mod.rs | 1 + interp/src/debugger/source/structures.rs | 2 +- interp/src/errors.rs | 86 ++----- interp/src/flatten/flat_ir/attributes.rs | 9 - interp/src/flatten/flat_ir/base.rs | 155 ++++++++++-- interp/src/flatten/flat_ir/cell_prototype.rs | 229 ++++++++++++++---- .../src/flatten/flat_ir/control/structures.rs | 61 ++++- .../src/flatten/flat_ir/control/translator.rs | 6 +- interp/src/flatten/flat_ir/identifier.rs | 23 +- interp/src/flatten/flat_ir/mod.rs | 13 +- interp/src/flatten/flat_ir/wires/core.rs | 15 ++ interp/src/flatten/flat_ir/wires/guards.rs | 11 + interp/src/flatten/flat_ir/wires/mod.rs | 2 + interp/src/flatten/mod.rs | 3 + interp/src/flatten/primitives/builder.rs | 49 ++-- interp/src/flatten/primitives/prim_trait.rs | 2 +- interp/src/flatten/structures/context.rs | 36 ++- .../src/flatten/structures/environment/env.rs | 7 +- interp/src/flatten/structures/printer.rs | 6 +- interp/src/main.rs | 2 + 27 files changed, 691 insertions(+), 258 deletions(-) delete mode 100644 interp/src/flatten/flat_ir/attributes.rs diff --git a/interp/src/configuration.rs b/interp/src/configuration.rs index 6cad252985..548418b75d 100644 --- a/interp/src/configuration.rs +++ b/interp/src/configuration.rs @@ -18,6 +18,20 @@ pub struct Config { } #[derive(Default)] +/// A builder for [`Config`] struct. +/// +/// ``` +/// # use interp::configuration::ConfigBuilder; +/// let config = ConfigBuilder::new() +/// .quiet(false) +/// .allow_invalid_memory_access(true) +/// .dump_all_memories(true) +/// .build(); +/// assert_eq!(config.quiet, false); +/// assert_eq!(config.allow_invalid_memory_access, true); +/// assert_eq!(config.dump_all_memories, true); +/// assert_eq!(config.dump_registers, false); +/// ``` pub struct ConfigBuilder { allow_invalid_memory_access: Option, error_on_overflow: Option, @@ -28,41 +42,50 @@ pub struct ConfigBuilder { } impl ConfigBuilder { + /// Create a new [`ConfigBuilder`] with all options unset. This is the same + /// as calling [`ConfigBuilder::default`]. #[inline] pub fn new() -> Self { Self::default() } + /// Sets the quiet flag to the given value. pub fn quiet(mut self, value: bool) -> Self { self.quiet = Some(value); self } + /// Sets the `allow_invalid_memory_access` flag to the given value. pub fn allow_invalid_memory_access(mut self, value: bool) -> Self { self.allow_invalid_memory_access = Some(value); self } + /// Sets the `error_on_overflow` flag to the given value. pub fn error_on_overflow(mut self, value: bool) -> Self { self.error_on_overflow = Some(value); self } + /// Sets the `allow_par_conflicts` flag to the given value. pub fn allow_par_conflicts(mut self, value: bool) -> Self { self.allow_par_conflicts = Some(value); self } + /// Sets the `dump_registers` flag to the given value. pub fn dump_registers(mut self, value: bool) -> Self { self.dump_registers = Some(value); self } - + /// Sets the `dump_all_memories` flag to the given value. pub fn dump_all_memories(mut self, value: bool) -> Self { self.dump_all_memories = Some(value); self } + /// Builds a [`Config`] from the current state of the [`ConfigBuilder`]. For + /// any unset options, the default value will be used. pub fn build(self) -> Config { Config { allow_par_conflicts: self.allow_par_conflicts.unwrap_or_default(), diff --git a/interp/src/debugger/commands/command_parser.rs b/interp/src/debugger/commands/command_parser.rs index d4445342ac..cd512c5a17 100644 --- a/interp/src/debugger/commands/command_parser.rs +++ b/interp/src/debugger/commands/command_parser.rs @@ -285,6 +285,7 @@ impl CommandParser { } } +/// Parse the given string into a debugger command. pub fn parse_command(input_str: &str) -> InterpreterResult { let inputs = CommandParser::parse(Rule::command, input_str)?; let input = inputs.single()?; diff --git a/interp/src/debugger/commands/commands.pest b/interp/src/debugger/commands/commands.pest index 7cc7df7ca2..799971648c 100644 --- a/interp/src/debugger/commands/commands.pest +++ b/interp/src/debugger/commands/commands.pest @@ -1,4 +1,4 @@ -WHITESPACE = _{ " " | "\t" | NEWLINE } +WHITESPACE = _{ " " | "\t" } dot = _{ "." } ident_syms = _{ "_" | "-" | "'" } num = @{ ASCII_DIGIT+ } @@ -21,10 +21,10 @@ print_code = { pc_fail = @{ "\\" ~ ASCII_ALPHANUMERIC* } print = { - (^"print" | ^"p") ~ (print_code)? ~ name+ + (^"print " | ^"p ") ~ (print_code)? ~ name+ } -print_state = { ^"print-state" ~ (print_code)? ~ name+ } +print_state = { ^"print-state " ~ (print_code)? ~ name+ } print_fail = { (^"print-state" | ^"print" | ^"p") ~ (print_code | pc_fail)? @@ -36,7 +36,7 @@ after = { ^"after" } watch_position = { before | after } watch = { - (^"watch" | ^"w") ~ (watch_position)? ~ group ~ ^"with" ~ (print_state | print) + (^"watch " | ^"w ") ~ (watch_position)? ~ group ~ (^"with")? ~ (print_state | print) } step_over = { ^"step-over" ~ group } @@ -52,18 +52,18 @@ display = { ^"display" | ^"d" } info_break = { (^"info" ~ ^"break") | ^"i" ~ ^"b" } info_watch = { (^"info" ~ ^"watch") | ^"i" ~ ^"w" } -brk = { (^"break" | ^"br") ~ group+ } +brk = { (^"break " | ^"br ") ~ group+ } brk_id = { (group | num) } -delete = { (^"delete" | ^"del") ~ brk_id+ } +delete = { (^"delete " | ^"del ") ~ brk_id+ } -delete_watch = { (^"delete-watch" | ^"del-watch") ~ brk_id+ } +delete_watch = { (^"delete-watch " | ^"del-watch " | ^"delw ") ~ brk_id+ } -enable = { (^"enable" | ^"en") ~ brk_id+ } -disable = { (^"disable" | ^"dis") ~ brk_id+ } +enable = { (^"enable " | ^"en ") ~ brk_id+ } +disable = { (^"disable " | ^"dis ") ~ brk_id+ } -enable_watch = { (^"enable-watch" | ^"enw") ~ brk_id+ } -disable_watch = { (^"disable-watch" | ^"disw") ~ brk_id+ } +enable_watch = { (^"enable-watch " | ^"enw ") ~ brk_id+ } +disable_watch = { (^"disable-watch " | ^"disw ") ~ brk_id+ } exit = { ^"exit" | ^"quit" } diff --git a/interp/src/debugger/commands/core.rs b/interp/src/debugger/commands/core.rs index 4cdc9da493..46f86279bb 100644 --- a/interp/src/debugger/commands/core.rs +++ b/interp/src/debugger/commands/core.rs @@ -1,3 +1,5 @@ +//! This module contains the core data structures and commands used by the debugger + use itertools::{self, Itertools}; use lazy_static::lazy_static; use owo_colors::OwoColorize; @@ -40,6 +42,8 @@ impl Display for WatchpointIdx { } } +/// The name of a group taken from user input. The component may be elided in +/// which case it is generally assumed to be the entry point. #[derive(Debug)] pub struct ParsedGroupName { component: Option, @@ -47,6 +51,7 @@ pub struct ParsedGroupName { } impl ParsedGroupName { + /// Create a new [ParsedGroupName] from just a group name. pub fn from_group_name(group: String) -> Self { Self { component: None, @@ -54,6 +59,7 @@ impl ParsedGroupName { } } + /// Create a new [ParsedGroupName] from a component and group name. pub fn from_comp_and_group(component: String, group: String) -> Self { Self { component: Some(component), @@ -61,28 +67,8 @@ impl ParsedGroupName { } } - pub fn is_concrete(&self) -> bool { - self.component.is_some() - } - - pub fn concretize(&self, component: String) -> GroupName { - GroupName { - component: self.component.as_ref().cloned().unwrap_or(component), - group: self.group.clone(), - } - } - - pub fn get_concrete(&self) -> Option { - if self.is_concrete() { - Some(GroupName { - component: self.component.as_ref().cloned().unwrap(), - group: self.group.clone(), - }) - } else { - None - } - } - + /// Attempts to look up the group of the given name in the context. If the + /// group lacks a component, it is assumed to be the entry point. pub fn lookup_group(&self, context: &Context) -> Result { let comp = if let Some(c) = &self.component { context @@ -98,18 +84,17 @@ impl ParsedGroupName { } } -#[derive(Debug, Clone)] -pub struct GroupName { - pub component: String, - pub group: String, -} - +/// An enum representing a breakpoint/watchpoint from user input. This may or +/// may not be valid. pub enum ParsedBreakPointID { + /// A breakpoint given by the group name. Name(ParsedGroupName), + /// A breakpoint given by the identifying number. Number(u32), } impl ParsedBreakPointID { + /// Attempts to parse the breakpoint from user input into a concrete [BreakpointID]. pub fn parse_to_break_ids( &self, context: &Context, @@ -125,6 +110,7 @@ impl ParsedBreakPointID { } } + /// Attempts to parse the watchpoint from user input into a concrete [WatchID]. pub fn parse_to_watch_ids( &self, context: &Context, @@ -153,12 +139,19 @@ impl From for ParsedBreakPointID { } } +/// A concrete breakpoint pub enum BreakpointID { + /// A breakpoint on the given group. This does not guarantee that there is + /// such a breakpoint, but it does guarantee that the group exists. Name(GroupIdx), + /// A breakpoint on the given ID. This does not guarantee that there is a + /// breakpoint by the given ID. In such cases, operations on the breakpoint + /// will produce an error. Number(BreakpointIdx), } impl BreakpointID { + /// Attempts to get the breakpoint ID as a number. #[must_use] pub fn as_number(&self) -> Option<&BreakpointIdx> { if let Self::Number(v) = self { @@ -168,6 +161,7 @@ impl BreakpointID { } } + /// Attempts to get the breakpoint ID as a group. #[must_use] pub fn as_name(&self) -> Option<&GroupIdx> { if let Self::Name(v) = self { @@ -178,12 +172,21 @@ impl BreakpointID { } } +/// A concrete watchpoint pub enum WatchID { + /// A watchpoint on the given group. This does not guarantee that there is + /// such a watchpoint, but it does guarantee that the group exists. Since + /// multiple watchpoints may exist for a single group, any operation applied + /// to this watchpoint will affect all of them. Name(GroupIdx), + /// A watchpoint on the given ID. This does not guarantee that there is a + /// watchpoint by the given ID. In such cases, operations on the watchpoint + /// will produce an error. Number(WatchpointIdx), } impl WatchID { + /// Attempts to get the watchpoint ID as a name. #[must_use] pub fn as_name(&self) -> Option<&GroupIdx> { if let Self::Name(v) = self { @@ -193,6 +196,7 @@ impl WatchID { } } + /// Attempts to get the watchpoint ID as a number. #[must_use] pub fn as_number(&self) -> Option<&WatchpointIdx> { if let Self::Number(v) = self { @@ -203,9 +207,12 @@ impl WatchID { } } +/// The position of a watchpoint relative to a group's execution. #[derive(Clone, Copy, Debug)] pub enum WatchPosition { + /// The watchpoint is placed at the beginning of the group execution. Before, + /// The watchpoint is placed at the end of the group execution. After, } @@ -215,27 +222,42 @@ impl Default for WatchPosition { } } +/// An enum representing what information the print command targets. #[derive(Debug, Clone, Copy)] pub enum PrintMode { + /// The print command targets the state of the cell. This only works for + /// cells which contain internal state such as registers or memories. State, + /// The print command targets the port information. This may be applied to a + /// single port, or the cell in which case all ports are printed. Port, } + +/// A tuple representing a print command. +/// +/// The tuple consists of a list of paths to the targets to print, an optional +/// print code used to format the information, and the print mode. #[derive(Debug, Clone)] pub struct PrintTuple(Vec, Option, PrintMode); impl PrintTuple { + /// Returns a reference to the list of targets to print. pub fn target(&self) -> &Vec { &self.0 } + /// Returns a reference to the print code. pub fn print_code(&self) -> &Option { &self.1 } + /// Returns a reference to the print mode. pub fn print_mode(&self) -> &PrintMode { &self.2 } + /// Return a formatted string representing the print tuple. Used to display + /// stored watchpoints to the user. pub fn format + Clone>( &self, env: &Environment, @@ -277,24 +299,44 @@ impl From<(Vec, Option, PrintMode)> for PrintTuple { } } +/// A command that can be sent to the debugger. pub enum Command { - Step(u32), // Step execution - Continue, // Execute until breakpoint - Empty, // Empty command, does nothing - Display, // Display full environment contents - Print(Vec>, Option, PrintMode), // Print something - Break(Vec), // Create a breakpoint - Help, // Help message - Exit, // Exit the debugger - InfoBreak, // List breakpoints + /// Advance the execution by a given number of steps (cycles). + Step(u32), + /// Execute until the next breakpoint. Or until the program finishes + Continue, + /// Empty command, does nothing. + Empty, + /// Display the full environment contents. Currently this command is defunct + Display, + /// Print out the value of the given target. Can be configured with + /// different modes and print formats. + Print(Vec>, Option, PrintMode), + /// Create a breakpoint on the given groups. + Break(Vec), + /// Display the help message. + Help, + /// Exit the debugger. + Exit, + /// List all breakpoints. + InfoBreak, + /// List all watchpoints. InfoWatch, + /// Disable the given breakpoints. Disable(Vec), + /// Enable the given breakpoints. Enable(Vec), + /// Delete the given breakpoints. Delete(Vec), + /// Enable the given watchpoints. EnableWatch(Vec), + /// Disable the given watchpoints. DisableWatch(Vec), + /// Delete the given watchpoints. DeleteWatch(Vec), + /// Advance the execution until the given group is no longer running. StepOver(ParsedGroupName), + /// Create a watchpoint Watch( ParsedGroupName, WatchPosition, @@ -302,8 +344,11 @@ pub enum Command { Option, PrintMode, ), + /// Print the current program counter PrintPC(bool), + /// Show command examples Explain, + /// Restart the debugger from the beginning of the execution. Command history, breakpoints, watchpoints, etc. are preserved. Restart, } @@ -312,21 +357,24 @@ type UsageExample = &'static str; type CommandName = &'static str; impl Command { + /// Returns the help message for the debugger. pub fn get_help_string() -> String { let mut out = String::new(); + for CommandInfo { invocation: names, description: message, .. } in COMMAND_INFO.iter() { - writeln!(out, " {: <20}{}", names.join(", "), message.green()) + writeln!(out, " {: <30}{}", names.join(", "), message.green()) .unwrap(); } out } + /// Returns the usage examples for the debugger. pub fn get_explain_string() -> String { let mut out = String::new(); for CommandInfo { @@ -413,19 +461,36 @@ lazy_static! { .description("Delete target breakpoint") .usage("> del 1").usage("> del do_add").build(), // enable - CIBuilder::new().invocation("enable") + CIBuilder::new().invocation("enable").invocation("en") .description("Enable target breakpoint") .usage("> enable 1").usage("> enable do_add").build(), // disable - CIBuilder::new().invocation("disable") + CIBuilder::new().invocation("disable").invocation("dis") .description("Disable target breakpoint") .usage("> disable 4").usage("> disable do_mult").build(), + + // del watch + CIBuilder::new().invocation("delete-watch").invocation("delw") + .description("Delete target watchpoint") + .usage("> delete-watch 1") + .usage("> delete-watch do_add").build(), + + CIBuilder::new().invocation("enable-watch").invocation("enw") + .description("Enable target watchpoint") + .usage("> enable-watch 1") + .usage("> enable-watch do_add").build(), + + CIBuilder::new().invocation("disable-watch").invocation("disw") + .description("Disable target watchpoint") + .usage("> disable-watch 4") + .usage("> disable-watch do_mult").build(), + // explain CIBuilder::new().invocation("explain") .description("Show examples of commands which take arguments").build(), CIBuilder::new().invocation("restart") - .description("Restart the debugger from the beginning of the execution. Command history, breakpoints, watchpoints, etc. are preserved").build(), - // exit/quit + .description("Restart the debugger from the beginning of the execution. Command history, breakpoints, watchpoints, etc. are preserved") + .build(), CIBuilder::new().invocation("exit") .invocation("quit") .description("Exit the debugger").build(), @@ -434,7 +499,7 @@ lazy_static! { } #[derive(Clone, Debug)] -pub struct CommandInfo { +struct CommandInfo { invocation: Vec, description: Description, usage_example: Vec, diff --git a/interp/src/debugger/commands/mod.rs b/interp/src/debugger/commands/mod.rs index 3598b066d6..452108f4ea 100644 --- a/interp/src/debugger/commands/mod.rs +++ b/interp/src/debugger/commands/mod.rs @@ -1,3 +1,4 @@ +//! This module contains the structures for the debugger commands pub(crate) mod command_parser; pub mod core; pub use command_parser::parse_command; diff --git a/interp/src/debugger/debugger_core.rs b/interp/src/debugger/debugger_core.rs index 08f2f4afa9..6b34dc8ef7 100644 --- a/interp/src/debugger/debugger_core.rs +++ b/interp/src/debugger/debugger_core.rs @@ -49,14 +49,19 @@ impl ProgramStatus { } } -/// An opaque wrapper type for internal debugging information +/// An opaque wrapper type for internal debugging information. This can only be +/// obtained by calling [Debugger::main_loop] and receiving a [DebuggerReturnStatus::Restart] return +/// value. pub struct DebuggerInfo { ctx: DebuggingContext, input_stream: Input, } - +/// An enum indicating the non-error return status of the debugger pub enum DebuggerReturnStatus { + /// Debugger exited with a restart command and should be reinitialized with + /// the returned information. Comes from [Command::Restart]. Restart(Box), + /// Debugger exited normally with an exit command. Comes from [Command::Exit]. Exit, } @@ -71,6 +76,8 @@ pub struct Debugger + Clone> { _source_map: Option, } +/// A type alias for the debugger using an Rc of the context. Use this in cases +/// where the use of lifetimes would be a hinderance. pub type OwnedDebugger = Debugger>; impl OwnedDebugger { @@ -93,6 +100,7 @@ impl OwnedDebugger { } impl + Clone> Debugger { + /// Construct a new debugger instance from the target calyx file pub fn new( program_context: C, data_file: &Option, @@ -214,6 +222,11 @@ impl + Clone> Debugger { // so on and so forth + /// The main loop of the debugger. This function is the entry point for the + /// debugger. It takes an optional [DebuggerInfo] struct which contains the + /// input stream and the debugging context which allows the debugger to + /// retain command history and other state after a restart. If not provided, + /// a fresh context and input stream will be used instead. pub fn main_loop( mut self, info: Option, diff --git a/interp/src/debugger/debugging_context/context.rs b/interp/src/debugger/debugging_context/context.rs index a8c30ae183..7f317d3aaa 100644 --- a/interp/src/debugger/debugging_context/context.rs +++ b/interp/src/debugger/debugging_context/context.rs @@ -276,6 +276,16 @@ impl WatchPointIndices { } } } + + fn is_empty(&self) -> bool { + match self { + Self::Before(idx) => idx.is_empty(), + Self::After(idx) => idx.is_empty(), + Self::Both { before, after } => { + before.is_empty() && after.is_empty() + } + } + } } #[derive(Debug)] @@ -369,6 +379,10 @@ impl WatchpointMap { after.retain(|i| *i != idx); } } + + if idxs.is_empty() { + self.group_idx_map.remove(&point.group); + } } } } diff --git a/interp/src/debugger/source/mod.rs b/interp/src/debugger/source/mod.rs index d7e23adc46..aac86a49de 100644 --- a/interp/src/debugger/source/mod.rs +++ b/interp/src/debugger/source/mod.rs @@ -1,3 +1,4 @@ +//! This module contains the modules used for source-code attribution pub(crate) mod metadata_parser; pub(crate) mod new_parser; pub mod structures; diff --git a/interp/src/debugger/source/structures.rs b/interp/src/debugger/source/structures.rs index f85c4dd703..44dc3a34bf 100644 --- a/interp/src/debugger/source/structures.rs +++ b/interp/src/debugger/source/structures.rs @@ -1,9 +1,9 @@ +//! This module contains the data structures used by the debugger for source mapping use std::{collections::HashMap, fs, path::PathBuf}; use crate::errors::InterpreterResult; #[derive(Hash, PartialEq, Eq, Debug, Clone)] - pub struct NamedTag(u64, String); impl NamedTag { diff --git a/interp/src/errors.rs b/interp/src/errors.rs index a891058af8..6c60a63e98 100644 --- a/interp/src/errors.rs +++ b/interp/src/errors.rs @@ -1,17 +1,20 @@ use crate::flatten::flat_ir::prelude::AssignedValue; use crate::values::Value; -use calyx_ir::{self as ir, Assignment, Id}; +use calyx_ir::Id; use calyx_utils::{Error as CalyxError, MultiError as CalyxMultiError}; use rustyline::error::ReadlineError; use thiserror::Error; -// Utility type +/// A type alias for a result with an [BoxedInterpreterError] as the error type pub type InterpreterResult = Result; +/// A wrapper type for [InterpreterError]. This exists to allow a smaller return +/// size for results since the error type is large. pub struct BoxedInterpreterError(Box); impl BoxedInterpreterError { - pub fn into_inner(&mut self) -> &mut InterpreterError { + /// Get a mutable reference to the inner error + pub fn inner_mut(&mut self) -> &mut InterpreterError { &mut self.0 } } @@ -51,6 +54,8 @@ where } } +/// An enum representing the different types of errors that can occur during +/// simulation and debugging #[derive(Error)] pub enum InterpreterError { /// The given debugger command is invalid/malformed @@ -74,13 +79,13 @@ pub enum InterpreterError { #[from] pest_consume::Error, ), - // Unable to parse metadata + /// Unable to parse metadata #[error(transparent)] NewMetadataParseError( #[from] pest_consume::Error, ), - // Missing metadata + /// Metadata is unavailable #[error("missing metadata")] MissingMetaData, @@ -100,20 +105,6 @@ pub enum InterpreterError { #[error("no main component")] MissingMainComponent, - /// Multiple assignments conflicting during interpretation - #[error( - "multiple assignments to one port: {parent_id}.{port_id} - Conflict between: - 1. {a1} - 2. {a2}" - )] - ConflictingAssignments { - port_id: Id, - parent_id: Id, - a1: String, - a2: String, - }, - #[error( "conflicting assigns 1. {a1} @@ -125,9 +116,7 @@ pub enum InterpreterError { a2: AssignedValue, }, - #[error("unable to find component named \"{0}\"")] - UnknownComponent(String), - + /// A currently defunct error type for cross branch conflicts #[error( "par assignments not disjoint: {parent_id}.{port_id} 1. {v1} @@ -139,14 +128,6 @@ pub enum InterpreterError { v1: Value, v2: Value, }, - #[error("invalid internal seq state. This should never happen, please report it")] - InvalidSeqState, - #[error( - "invalid internal if state. This should never happen, please report it" - )] - InvalidIfState, - #[error("invalid internal while state. This should never happen, please report it")] - InvalidWhileState, #[error("{mem_dim} Memory given initialization data with invalid dimension. When flattened, expected {expected} entries, but the memory was supplied with {given} entries instead. @@ -157,78 +138,49 @@ pub enum InterpreterError { given: usize, }, - #[error("interpreter does not have an implementation of the \"{0}\" primitive. If the interpreter should have an implementation of this primitive please open a github issue or PR.")] - UnknownPrimitive(String), - #[error("program evaluated the truth value of a wire \"{}.{}\" which is not one bit. Wire is {} bits wide.", 0.0, 0.1, 1)] - InvalidBoolCast((Id, Id), u64), - #[error("the interpreter attempted to exit the group \"{0}\" before it finished. This should never happen, please report it.")] - InvalidGroupExitNamed(Id), - #[error("the interpreter attempted to exit a phantom group before it finished. This should never happen, please report it")] - InvalidGroupExitUnnamed, - #[error("invalid memory access to memory {}. Given index ({}) but memory has dimension ({})", name, access.iter().map(|x| x.to_string()).collect::>().join(", "), dims.iter().map(|x| x.to_string()).collect::>().join(", "))] InvalidMemoryAccess { access: Vec, dims: Vec, name: Id, }, - #[error("Both read and write signals provided to the sequential memory.")] - SeqMemoryError, // TODO (Griffin): Make this error message better please #[error("Computation has under/overflowed its bounds")] OverflowError, + /// A wrapper for IO errors #[error(transparent)] IOError(#[from] std::io::Error), + /// The error for attempting to write `undef` values to a register or + /// memory. Contains the name of the register or memory as a string //TODO Griffin: Make this more descriptive #[error( "Attempted to write an undefined value to register or memory named \"{0}\"" )] UndefinedWrite(String), + /// The error for attempting to write to an undefined memory address. This + /// is distinct from writing to an out of bounds address. //TODO Griffin: Make this more descriptive #[error( "Attempted to write an undefined memory address in memory named \"{0}\"" )] UndefinedWriteAddr(String), - // TODO Griffin: Make this more descriptive + /// The error for attempting to read from an undefined memory address. This + /// is distinct from reading from an out of bounds address. #[error( "Attempted to read an undefined memory address from memory named \"{0}\"" )] UndefinedReadAddr(String), + /// A wrapper for serialization errors #[error(transparent)] SerializationError(#[from] crate::serialization::SerializationError), } -pub fn assignment_to_string( - assignment: &ir::Assignment, -) -> String { - let mut str = vec![]; - ir::Printer::write_assignment(assignment, 0, &mut str) - .expect("Write Failed"); - String::from_utf8(str).expect("Found invalid UTF-8") -} - -impl InterpreterError { - pub fn conflicting_assignments( - port_id: Id, - parent_id: Id, - a1: &Assignment, - a2: &Assignment, - ) -> Self { - Self::ConflictingAssignments { - port_id, - parent_id, - a1: assignment_to_string(a1), - a2: assignment_to_string(a2), - } - } -} - // this is silly but needed to make the program print something sensible when returning // a result from `main` impl std::fmt::Debug for InterpreterError { diff --git a/interp/src/flatten/flat_ir/attributes.rs b/interp/src/flatten/flat_ir/attributes.rs deleted file mode 100644 index 19f5f95ae8..0000000000 --- a/interp/src/flatten/flat_ir/attributes.rs +++ /dev/null @@ -1,9 +0,0 @@ -// use std::collections::HashMap; - -// use super::prelude::*; - -// #[derive(Debug, Clone, Default)] -// pub struct Attributes { -// // We don't need deterministic iteration or global position for the interpreter -// attrs: HashMap, -// } diff --git a/interp/src/flatten/flat_ir/base.rs b/interp/src/flatten/flat_ir/base.rs index 35ca1b68db..08ae8969db 100644 --- a/interp/src/flatten/flat_ir/base.rs +++ b/interp/src/flatten/flat_ir/base.rs @@ -21,44 +21,64 @@ use super::{cell_prototype::CellPrototype, prelude::Identifier}; pub struct ComponentIdx(u32); impl_index!(ComponentIdx); -/// An index for auxillary definition information for cells +/// An index for auxillary definition information for cells. This is used to +/// index into the [`SecondaryContext`][] +/// +/// [`SecondaryContext`]: crate::flatten::structures::context::SecondaryContext::local_cell_defs #[derive(Debug, Eq, Copy, Clone, PartialEq, Hash, PartialOrd, Ord)] pub struct CellDefinitionIdx(u32); impl_index!(CellDefinitionIdx); -/// An index for auxillary definition information for ports +/// An index for auxillary definition information for ports. This is used to +/// index into the [`SecondaryContext`][] +/// +/// [`SecondaryContext`]: crate::flatten::structures::context::SecondaryContext::local_port_defs #[derive(Debug, Eq, Copy, Clone, PartialEq, Hash, PartialOrd, Ord)] pub struct PortDefinitionIdx(u32); impl_index!(PortDefinitionIdx); -/// An index for auxillary definition information for ref cells +/// An index for auxillary definition information for ref cells. This is used to +/// index into the [`SecondaryContext`][] +/// +/// [`SecondaryContext`]: crate::flatten::structures::context::SecondaryContext::ref_cell_defs #[derive(Debug, Eq, Copy, Clone, PartialEq, Hash, PartialOrd, Ord)] pub struct RefCellDefinitionIdx(u32); impl_index!(RefCellDefinitionIdx); -/// An index for auxillary definition information for ref ports +/// An index for auxillary definition information for ref ports. This is used to +/// index into the [`SecondaryContext`][] +/// +/// [`SecondaryContext`]: crate::flatten::structures::context::SecondaryContext::ref_port_defs #[derive(Debug, Eq, Copy, Clone, PartialEq, Hash, PartialOrd, Ord)] pub struct RefPortDefinitionIdx(u32); impl_index!(RefPortDefinitionIdx); // Global indices -/// The index of a port instance in the global value map +/// The index of a port instance in the global value map. Used to index into the [`Environment`][] +/// +/// [`Environment`]: crate::flatten::structures::environment::Environment #[derive(Debug, Eq, Copy, Clone, PartialEq, Hash, PartialOrd, Ord)] pub struct GlobalPortIdx(NonZeroU32); impl_index_nonzero!(GlobalPortIdx); -/// The index of a cell instance in the global value map +/// The index of a cell instance in the global value map. Used to index into the [`Environment`][] +/// +/// [`Environment`]: crate::flatten::structures::environment::Environment #[derive(Debug, Eq, Copy, Clone, PartialEq, Hash, PartialOrd, Ord)] pub struct GlobalCellIdx(NonZeroU32); impl_index_nonzero!(GlobalCellIdx); -/// The index of a ref cell instance in the global value map +/// The index of a ref cell instance in the global value map. Used to index into the [`Environment`][] +/// +/// [`Environment`]: crate::flatten::structures::environment::Environment #[derive(Debug, Eq, Copy, Clone, PartialEq, Hash, PartialOrd, Ord)] pub struct GlobalRefCellIdx(u32); impl_index!(GlobalRefCellIdx); -/// The index of a ref port instance in the global value map +/// The index of a ref port instance in the global value map. Used to index into the [`Environment`][] +/// +/// [`Environment`]: crate::flatten::structures::environment::Environment #[derive(Debug, Eq, Copy, Clone, PartialEq, Hash, PartialOrd, Ord)] pub struct GlobalRefPortIdx(u32); impl_index!(GlobalRefPortIdx); @@ -67,14 +87,16 @@ impl_index!(GlobalRefPortIdx); /// A local port offset for a component. These are used in the definition of /// assignments and can only be understood in the context of the component they -/// are defined under. +/// are defined under. Combined with a base index from a component instance this +/// can be resolved to a [`GlobalPortIdx`]. #[derive(Debug, Eq, Copy, Clone, PartialEq, Hash, PartialOrd, Ord)] pub struct LocalPortOffset(u32); impl_index!(LocalPortOffset); /// A local ref port offset for a component. These are used in the definition of /// assignments and can only be understood in the context of the component they -/// are defined under. +/// are defined under. Combined with a base index from a component instance this +/// can be resolved to a [`GlobalRefPortIdx`]. #[derive(Debug, Eq, Copy, Clone, PartialEq, Hash, PartialOrd, Ord)] pub struct LocalRefPortOffset(u32); impl_index!(LocalRefPortOffset); @@ -102,6 +124,8 @@ pub enum PortRef { } impl PortRef { + /// Returns the local offset of the port reference if it is a local port + /// reference. Otherwise returns `None`. #[must_use] pub fn as_local(&self) -> Option<&LocalPortOffset> { if let Self::Local(v) = self { @@ -111,6 +135,8 @@ impl PortRef { } } + /// Returns the local offset of the port reference if it is a ref port + /// reference. Otherwise returns `None`. #[must_use] pub fn as_ref(&self) -> Option<&LocalRefPortOffset> { if let Self::Ref(v) = self { @@ -120,10 +146,14 @@ impl PortRef { } } + /// Returns the local port offset of the port reference if it is a local port + /// reference. Otherwise panics. pub fn unwrap_local(&self) -> &LocalPortOffset { self.as_local().unwrap() } + /// Returns the local ref port offset of the port reference if it is a ref port + /// reference. Otherwise panics. pub fn unwrap_ref(&self) -> &LocalRefPortOffset { self.as_ref().unwrap() } @@ -151,6 +181,8 @@ pub enum GlobalPortRef { } impl GlobalPortRef { + /// Constructs a global port reference from a local port reference and a base + /// index. pub fn from_local(local: PortRef, base_info: &BaseIndices) -> Self { match local { PortRef::Local(l) => (base_info + l).into(), @@ -172,6 +204,8 @@ impl From for GlobalPortRef { } impl GlobalPortRef { + /// Returns the global port index of the port reference if it is a global port + /// reference. Otherwise returns `None`. #[must_use] pub fn as_port(&self) -> Option<&GlobalPortIdx> { if let Self::Port(v) = self { @@ -180,19 +214,12 @@ impl GlobalPortRef { None } } - - #[must_use] - pub fn _as_ref(&self) -> Option<&GlobalRefPortIdx> { - if let Self::Ref(v) = self { - Some(v) - } else { - None - } - } } /// An enum wrapping the two different types of port definitions (ref/local) pub enum PortDefinitionRef { + /// A local port definition Local(PortDefinitionIdx), + /// A ref port definition Ref(RefPortDefinitionIdx), } @@ -215,11 +242,15 @@ impl From for PortDefinitionRef { /// because of alignment #[derive(Debug, Copy, Clone)] pub enum CellRef { + /// A local cell offset Local(LocalCellOffset), + /// A ref cell offset Ref(LocalRefCellOffset), } impl CellRef { + /// Returns the local cell offset if it is a local cell reference. Otherwise + /// returns `None`. #[must_use] pub fn as_local(&self) -> Option<&LocalCellOffset> { if let Self::Local(v) = self { @@ -229,6 +260,8 @@ impl CellRef { } } + /// Returns the local ref cell offset if it is a ref cell reference. Otherwise + /// returns `None`. #[must_use] pub fn as_ref(&self) -> Option<&LocalRefCellOffset> { if let Self::Ref(v) = self { @@ -251,9 +284,13 @@ impl From for CellRef { } } +/// An enum wrapping the two different type of global cell references +/// (ref/local). This is the global analogue to [CellRef]. #[derive(Debug)] pub enum GlobalCellRef { + /// A global cell index Cell(GlobalCellIdx), + /// A global ref cell index Ref(GlobalRefCellIdx), } @@ -270,6 +307,8 @@ impl From for GlobalCellRef { } impl GlobalCellRef { + /// Constructs a global cell reference from a local cell reference and a base + /// index. pub fn from_local(local: CellRef, base_info: &BaseIndices) -> Self { match local { CellRef::Local(l) => (base_info + l).into(), @@ -277,6 +316,8 @@ impl GlobalCellRef { } } + /// Returns the global cell index if the reference is a global cell + /// reference. Otherwise returns `None`. #[must_use] pub fn as_cell(&self) -> Option<&GlobalCellIdx> { if let Self::Cell(v) = self { @@ -286,6 +327,8 @@ impl GlobalCellRef { } } + /// Returns the global ref cell index if the reference is a global ref cell + /// reference. Otherwise returns `None`. #[must_use] pub fn as_ref(&self) -> Option<&GlobalRefCellIdx> { if let Self::Ref(v) = self { @@ -295,7 +338,7 @@ impl GlobalCellRef { } } - /// Returns `true` if the global cell ref is [`Cell`]. + /// Returns `true` if the global cell ref is [`Cell`][]. /// /// [`Cell`]: GlobalCellRef::Cell #[must_use] @@ -303,7 +346,7 @@ impl GlobalCellRef { matches!(self, Self::Cell(..)) } - /// Returns `true` if the global cell ref is [`Ref`]. + /// Returns `true` if the global cell ref is [`Ref`][]. /// /// [`Ref`]: GlobalCellRef::Ref #[must_use] @@ -312,8 +355,11 @@ impl GlobalCellRef { } } +/// An enum wrapping the two different type of cell definitions (ref/local) pub enum CellDefinitionRef { + /// A local cell definition Local(CellDefinitionIdx), + /// A ref cell definition Ref(RefCellDefinitionIdx), } @@ -334,6 +380,9 @@ impl From for CellDefinitionRef { pub struct AssignmentIdx(u32); impl_index!(AssignmentIdx); +/// An enum representing the "winner" of an assignment. This tells us how the +/// value was assigned to the port. For standard assignments, this is also used +/// to detect conflicts where there are multiple driving assignments. #[derive(Debug, Clone, PartialEq, Eq)] pub enum AssignmentWinner { /// Indicates that the "winning" assignment for this port was produced by a @@ -341,7 +390,8 @@ pub enum AssignmentWinner { /// ports, there is no way for multiple cells to write the same output port, /// thus we don't need to record the cell that assigned it. Cell, - /// A concrete value produced by the control program + /// A concrete value produced by the control program or from an external + /// source Implicit, /// The assignment that produced this value. Assign(AssignmentIdx), @@ -353,6 +403,8 @@ impl From for AssignmentWinner { } } +/// A struct representing a value that has been assigned to a port. It wraps a +/// concrete value and the "winner" which assigned it. #[derive(Clone, PartialEq)] pub struct AssignedValue { val: Value, @@ -376,6 +428,7 @@ impl std::fmt::Display for AssignedValue { } impl AssignedValue { + /// Creates a new AssignedValue pub fn new>(val: Value, winner: T) -> Self { Self { val, @@ -388,14 +441,18 @@ impl AssignedValue { self.winner != other.winner } + /// Returns the value of the assigned value pub fn val(&self) -> &Value { &self.val } + /// Returns the winner of the assigned value pub fn winner(&self) -> &AssignmentWinner { &self.winner } + /// A utility constructor which returns a new implicitly assigned value with + /// a one bit high value pub fn implicit_bit_high() -> Self { Self { val: Value::bit_high(), @@ -403,6 +460,8 @@ impl AssignedValue { } } + /// A utility constructor which returns an [`AssignedValue`] with the given + /// value and a [`AssignmentWinner::Cell`] as the winner #[inline] pub fn cell_value(val: Value) -> Self { Self { @@ -411,6 +470,8 @@ impl AssignedValue { } } + /// A utility constructor which returns an [`AssignedValue`] with the given + /// value and a [`AssignmentWinner::Implicit`] as the winner #[inline] pub fn implicit_value(val: Value) -> Self { Self { @@ -419,11 +480,14 @@ impl AssignedValue { } } + /// A utility constructor which returns an [`AssignedValue`] with a one bit + /// high value and a [`AssignmentWinner::Cell`] as the winner #[inline] pub fn cell_b_high() -> Self { Self::cell_value(Value::bit_high()) } - + /// A utility constructor which returns an [`AssignedValue`] with a one bit + /// low value and a [`AssignmentWinner::Cell`] as the winner #[inline] pub fn cell_b_low() -> Self { Self::cell_value(Value::bit_low()) @@ -431,7 +495,8 @@ impl AssignedValue { } #[derive(Debug, Clone, Default)] -/// A wrapper struct around an option of an [AssignedValue] +/// A wrapper struct around an option of an [AssignedValue]. In the case where +/// the option is [`None`], the value is taken to be undefined. pub struct PortValue(Option); impl std::fmt::Display for PortValue { @@ -441,38 +506,55 @@ impl std::fmt::Display for PortValue { } impl PortValue { + /// Returns true if the value is undefined. This is the inverse of + /// [`PortValue::is_def`] pub fn is_undef(&self) -> bool { self.0.is_none() } + /// Returns true if the value is defined. This is the inverse of + /// [`PortValue::is_undef`] pub fn is_def(&self) -> bool { self.0.is_some() } + /// Returns a reference to the underlying [`AssignedValue`] if it is defined. + /// Otherwise returns `None`. pub fn as_option(&self) -> Option<&AssignedValue> { self.0.as_ref() } + /// If the value is defined, returns the value cast to a boolean. Otherwise + /// returns `None`. It uses the [`Value::as_bool`] method and will panic if + /// the given value is not one bit wide. pub fn as_bool(&self) -> Option { self.0.as_ref().map(|x| x.val().as_bool()) } + /// If the value is defined, returns the value cast to a usize. Otherwise + /// returns `None`. It uses the [`Value::as_usize`] method. pub fn as_usize(&self) -> Option { self.0.as_ref().map(|x| x.val().as_usize()) } + /// Returns a reference to the underlying value if it is defined. Otherwise + /// returns `None`. pub fn val(&self) -> Option<&Value> { self.0.as_ref().map(|x| &x.val) } + /// Returns a reference to the underlying [`AssignmentWinner`] if it is + /// defined. Otherwise returns `None`. pub fn winner(&self) -> Option<&AssignmentWinner> { self.0.as_ref().map(|x| &x.winner) } + /// Creates a new PortValue from the given value pub fn new>(val: T) -> Self { val.into() } + /// Creates a new undefined [PortValue] pub fn new_undef() -> Self { Self(None) } @@ -482,6 +564,7 @@ impl PortValue { Self(Some(AssignedValue::cell_value(val))) } + /// Creates a width-bit zero [PortValue] that has the "winner" as a cell pub fn new_cell_zeroes(width: u32) -> Self { Self::new_cell(Value::zeroes(width)) } @@ -497,6 +580,8 @@ impl PortValue { self.0.take() } + /// Formats the value according to the given [PrintCode] and returns the + /// resultant string. This is used by the debugger. pub fn format_value(&self, print_code: PrintCode) -> String { if let Some(v) = self.0.as_ref() { let v = &v.val; @@ -549,14 +634,19 @@ impl_index_nonzero!(CombGroupIdx); pub struct GuardIdx(u32); impl_index!(GuardIdx); +/// A struct containing information about a cell definition. #[derive(Debug, Clone)] pub struct CellDefinitionInfo where C: sealed::PortType, { + /// The name of the cell pub name: Identifier, + /// The ports defined by the cell pub ports: IndexRange, + /// The component in which the cell is defined pub parent: ComponentIdx, + /// The prototype of the cell pub prototype: CellPrototype, } @@ -564,6 +654,7 @@ impl CellDefinitionInfo where C: sealed::PortType, { + /// Constructs a new CellDefinitionInfo instance pub fn new( name: Identifier, ports: IndexRange, @@ -579,13 +670,20 @@ where } } +/// A type alias for a local cell definition pub type CellInfo = CellDefinitionInfo; +/// A type alias for a ref cell definition pub type RefCellInfo = CellDefinitionInfo; +/// An enum wrapping the possible parents of a port pub enum ParentIdx { + /// The port is part of a component signature Component(ComponentIdx), + /// The port belongs to a cell Cell(CellDefinitionIdx), + /// The port belongs to a ref cell RefCell(RefCellDefinitionIdx), + /// The port belongs to a group, i.e. is a hole Group(GroupIdx), } @@ -613,7 +711,7 @@ impl From for ParentIdx { } } -// don't look at this. Seriously +/// don't look at this. Seriously mod sealed { use crate::flatten::structures::index_trait::IndexRef; @@ -625,15 +723,22 @@ mod sealed { impl PortType for LocalRefPortOffset {} } +/// A struct wrapping the base index of all global port and cell indices. This +/// defines the start point for a component instance. #[derive(Debug, Clone)] pub struct BaseIndices { + /// The local port starting index pub port_base: GlobalPortIdx, + /// The local cell starting index pub cell_base: GlobalCellIdx, + /// The ref cell starting index pub ref_cell_base: GlobalRefCellIdx, + /// The ref port starting index pub ref_port_base: GlobalRefPortIdx, } impl BaseIndices { + /// Creates a new BaseIndices instance pub fn new( port_base: GlobalPortIdx, cell_base: GlobalCellIdx, diff --git a/interp/src/flatten/flat_ir/cell_prototype.rs b/interp/src/flatten/flat_ir/cell_prototype.rs index ee2d26d1d8..d7feea753d 100644 --- a/interp/src/flatten/flat_ir/cell_prototype.rs +++ b/interp/src/flatten/flat_ir/cell_prototype.rs @@ -7,117 +7,214 @@ use crate::{ use super::prelude::ComponentIdx; +/// Whether a constant is a literal or a primitive #[derive(Debug, Clone)] -pub enum LiteralOrPrimitive { +pub enum ConstantType { + /// A literal constant Literal, + /// A use of the primitive `std_const` Primitive, } /// An enum for encoding primitive operator types with only one width parameter #[derive(Debug, Clone)] pub enum SingleWidthType { + /// A register (`std_reg`) Reg, - // + /// Bitwise not (`std_not`) Not, + /// Bitwise and (`std_and`) And, + /// Bitwise or (`std_or`) Or, + /// Bitwise xor (`std_xor`) Xor, - // + /// Addition (`std_add`) Add, + /// Subtraction (`std_sub`) Sub, + /// Greater than (`std_gt`) Gt, + /// Less than (`std_lt`) Lt, + /// Equality (`std_eq`) Eq, + /// Inequality (`std_neq`) Neq, + /// Greater than or equal to (`std_ge`) Ge, + /// Less than or equal to (`std_le`) Le, - // + /// Left shift (`std_lsh`) Lsh, + /// Right shift (`std_rsh`) Rsh, + /// Multiplexer (`std_mux`) Mux, + /// Wire (`std_wire`) Wire, - // + /// Signed addition (`std_sadd`) SignedAdd, + /// Signed subtraction (`std_ssub`) SignedSub, + /// Signed greater than (`std_sgt`) SignedGt, + /// Signed less than (`std_slt`) SignedLt, + /// Signed equality (`std_seq`) SignedEq, + /// Signed inequality (`std_sneq`) SignedNeq, + /// Signed greater than or equal to (`std_sge`) SignedGe, + /// Signed less than or equal to (`std_sle`) SignedLe, + /// Signed left shift (`std_slsh`) SignedLsh, + /// Signed right shift (`std_srsh`) SignedRsh, + /// Multiplication pipe (`std_mult_pipe`) MultPipe, + /// Signed multiplication pipe (`std_signed_mult_pipe`) SignedMultPipe, + /// Division pipe (`std_div_pipe`) DivPipe, + /// Signed division pipe (`std_signed_div_pipe`) SignedDivPipe, + /// Square root (`std_sqrt`) Sqrt, - // + /// Unsynthesizeable multiplication (`std_unsyn_mult`) UnsynMult, + /// Unsynthesizeable division (`std_unsyn_div`) UnsynDiv, + /// Unsynthesizeable mod (`std_unsyn_mod`) UnsynMod, + /// Unsynthesizeable signed multiplication (`std_unsyn_smult`) UnsynSMult, + /// Unsynthesizeable signed division (`std_unsyn_sdiv`) UnsynSDiv, + /// Unsynthesizeable signed mod (`std_unsyn_smod`) UnsynSMod, + /// Represents the `undef` primitive. Not to be confused with undefined + /// port values during simulation. Undef, } +/// An enum for encoding primitive operator types with two width parameters +#[derive(Debug, Clone)] +pub enum DoubleWidthType { + /// 1: input width, 2: output width + Slice, + /// 1: input width, 2: output width + Pad, +} + +/// An enum for encoding primitive operator types with three width parameters +#[derive(Debug, Clone)] +pub enum TripleWidthType { + /// 1: left width, 2: right width, 3: output width + Cat, + /// 1: start index, 2: end index, 3: output width + BitSlice, +} + /// An enum for encoding FP primitives operator types #[derive(Debug, Clone)] pub enum FXType { + /// Addition (`std_fp_add`) Add, + /// Subtraction (`std_fp_sub`) Sub, + /// Multiplication (`std_fp_mult`) Mult, + /// Division (`std_fp_div`) Div, + /// Signed addition (`std_fp_sadd`) SignedAdd, + /// Signed subtraction (`std_fp_ssub`) SignedSub, + /// Signed multiplication (`std_fp_smult`) SignedMult, + /// Signed division (`std_fp_sdiv`) SignedDiv, + /// Greater than (`std_fp_gt`) Gt, + /// Signed greater than (`std_fp_sgt`) SignedGt, + /// Signed less than (`std_fp_slt`) SignedLt, + /// Square root (`std_fp_sqrt`) Sqrt, } +/// An enum for encoding memory primitives operator types #[derive(Debug, Clone)] pub enum MemType { + /// Sequential memory (`seq_mem_dX`) Seq, + /// Combinational memory (`comb_mem_dX`) Std, } +/// The dimensions of a memory primitive #[derive(Debug, Clone)] pub enum MemoryDimensions { + /// 1-dimensional memory D1 { + /// Size of the first dimension d0_size: ParamWidth, + /// Size of the first index d0_idx_size: ParamWidth, }, + /// 2-dimensional memory D2 { + /// Size of the first dimension d0_size: ParamWidth, + /// Size of the first index d0_idx_size: ParamWidth, + /// Size of the second dimension d1_size: ParamWidth, + /// Size of the second index d1_idx_size: ParamWidth, }, + /// 3-dimensional memory D3 { + /// Size of the first dimension d0_size: ParamWidth, + /// Size of the first index d0_idx_size: ParamWidth, + /// Size of the second dimension d1_size: ParamWidth, + /// Size of the second index d1_idx_size: ParamWidth, + /// Size of the third dimension d2_size: ParamWidth, + /// Size of the third index d2_idx_size: ParamWidth, }, + /// 4-dimensional memory D4 { + /// Size of the first dimension d0_size: ParamWidth, + /// Size of the first index d0_idx_size: ParamWidth, + /// Size of the second dimension d1_size: ParamWidth, + /// Size of the second index d1_idx_size: ParamWidth, + /// Size of the third dimension d2_size: ParamWidth, + /// Size of the third index d2_idx_size: ParamWidth, + /// Size of the fourth dimension d3_size: ParamWidth, + /// Size of the fourth index d3_idx_size: ParamWidth, }, } impl MemoryDimensions { + /// Returns the total number of entries in the memory pub fn size(&self) -> usize { match self { Self::D1 { d0_size, .. } => *d0_size as usize, @@ -183,52 +280,76 @@ impl MemoryDimensions { /// A type alias to allow potential space hacks pub type ParamWidth = u32; +/// Represents the type of a Calyx cell and contains its definition information #[derive(Debug, Clone)] pub enum CellPrototype { + /// This cell is an instance of a Calyx component Component(ComponentIdx), + /// This cell is a constant. Either constant literal or use of the primitive `std_const` Constant { + /// The value of the constant value: u64, + /// The width of the value width: ParamWidth, - c_type: LiteralOrPrimitive, + /// Whether the constant is a literal or a primitive + c_type: ConstantType, }, + /// This cell is a primitive type that only has a single width parameter. + /// See [`SingleWidthType`] for the list of primitives. SingleWidth { + /// The operator op: SingleWidthType, + /// The width parameter of the operator width: ParamWidth, }, + /// This cell is a primitive type that has two width parameters. + /// See [`DoubleWidthType`] for the list of primitives. + DoubleWidth { + /// The operator + op: DoubleWidthType, + /// The first width parameter of the operator + width1: ParamWidth, + /// The second width parameter of the operator + width2: ParamWidth, + }, + /// This cell is a primitive type that has three width parameters. + /// See [`TripleWidthType`] for the list of primitives. + TripleWidth { + /// The operator + op: TripleWidthType, + /// The first width parameter of the operator + width1: ParamWidth, + /// The second width parameter of the operator + width2: ParamWidth, + /// The third width parameter of the operator + width3: ParamWidth, + }, + /// This cell is a fixed point primitive. See [`FXType`] for the list of primitives. FixedPoint { + /// Fixed point operator op: FXType, + // TODO griffin: Consider deleting width + /// The width of the fixed point width: ParamWidth, + /// The width of the integer part int_width: ParamWidth, + /// The width of the fractional part frac_width: ParamWidth, }, - // The awkward three that don't fit the other patterns - Slice { - in_width: ParamWidth, - out_width: ParamWidth, - }, - Pad { - in_width: ParamWidth, - out_width: ParamWidth, - }, - Cat { - left: ParamWidth, - right: ParamWidth, - out: ParamWidth, - }, - BitSlice { - start_idx: ParamWidth, - end_idx: ParamWidth, - out_width: ParamWidth, - }, - // Memories + /// This cell is a memory primitive. Either a combinational or sequential memory. Memory { + /// The type of memory mem_type: MemType, + /// The width of the values in the memory width: ParamWidth, + /// The dimensions of the memory dims: MemoryDimensions, + /// Is the memory external? is_external: bool, }, - // TODO Griffin: lots more + /// This cell is a primitive that lacks an implementation in Cider. Its name + /// and parameter bindings are stored for use in error messages. Unknown(String, Box), } @@ -239,6 +360,7 @@ impl From for CellPrototype { } impl CellPrototype { + /// Returns the component index if this is a component otherwise `None` #[must_use] pub fn as_component(&self) -> Option<&ComponentIdx> { if let Self::Component(v) = self { @@ -248,8 +370,9 @@ impl CellPrototype { } } + /// Constructs a prototype for the given cell #[must_use] - pub fn construct_primitive(cell: &cir::Cell) -> Self { + pub fn construct_prototype(cell: &cir::Cell) -> Self { if let cir::CellType::Primitive { name, param_binding, @@ -277,7 +400,7 @@ impl CellPrototype { Self::Constant { value, width: width.try_into().unwrap(), - c_type: LiteralOrPrimitive::Primitive, + c_type: ConstantType::Primitive, } } n @ ("std_add" | "std_sadd") => { @@ -502,9 +625,10 @@ impl CellPrototype { out_width: "OUT_WIDTH" ]; - Self::Slice { - in_width: in_width.try_into().unwrap(), - out_width: out_width.try_into().unwrap(), + Self::DoubleWidth { + op: DoubleWidthType::Slice, + width1: in_width.try_into().unwrap(), + width2: out_width.try_into().unwrap(), } } "std_pad" => { @@ -513,9 +637,10 @@ impl CellPrototype { out_width: "OUT_WIDTH" ]; - Self::Pad { - in_width: in_width.try_into().unwrap(), - out_width: out_width.try_into().unwrap(), + Self::DoubleWidth { + op: DoubleWidthType::Pad, + width1: in_width.try_into().unwrap(), + width2: out_width.try_into().unwrap(), } } "std_cat" => { @@ -524,10 +649,11 @@ impl CellPrototype { right_width: "RIGHT_WIDTH", out_width: "OUT_WIDTH" ]; - Self::Cat { - left: left_width.try_into().unwrap(), - right: right_width.try_into().unwrap(), - out: out_width.try_into().unwrap(), + Self::TripleWidth { + op: TripleWidthType::Cat, + width1: left_width.try_into().unwrap(), + width2: right_width.try_into().unwrap(), + width3: out_width.try_into().unwrap(), } } n @ ("comb_mem_d1" | "seq_mem_d1") => { @@ -674,10 +800,11 @@ impl CellPrototype { end_idx: "END_IDX", out_width: "OUT_WIDTH" ]; - Self::BitSlice { - start_idx: start_idx.try_into().unwrap(), - end_idx: end_idx.try_into().unwrap(), - out_width: out_width.try_into().unwrap(), + Self::TripleWidth { + op: TripleWidthType::BitSlice, + width1: start_idx.try_into().unwrap(), + width2: end_idx.try_into().unwrap(), + width3: out_width.try_into().unwrap(), } } @@ -698,4 +825,18 @@ impl CellPrototype { pub fn is_component(&self) -> bool { matches!(self, Self::Component(..)) } + + /// Returns `true` if the cell prototype is a [`Constant`] constructed from + /// a literal. + /// + /// [`Constant`]: CellPrototype::Constant + pub fn is_literal_constant(&self) -> bool { + matches!( + self, + Self::Constant { + c_type: ConstantType::Literal, + .. + } + ) + } } diff --git a/interp/src/flatten/flat_ir/control/structures.rs b/interp/src/flatten/flat_ir/control/structures.rs index 9a17a4f33e..cc2d038841 100644 --- a/interp/src/flatten/flat_ir/control/structures.rs +++ b/interp/src/flatten/flat_ir/control/structures.rs @@ -8,6 +8,7 @@ use crate::flatten::{ }, }; +/// An index representing a control statement #[derive(Debug, Eq, Copy, Clone, PartialEq, Hash, PartialOrd)] pub struct ControlIdx(u32); impl_index!(ControlIdx); @@ -22,25 +23,28 @@ pub type CtrlVec = SmallVec<[ControlIdx; 4]>; #[derive(Debug)] pub struct Empty; -/// A group enable node +/// A group enable node. Analogue of [calyx_ir::Enable] #[derive(Debug)] pub struct Enable(GroupIdx); impl Enable { + /// Returns the group index of the enable statement pub fn group(&self) -> GroupIdx { self.0 } + /// Create a new enable statement pub fn new(group: GroupIdx) -> Self { Self(group) } } -/// Sequence of control nodes +/// Sequence of control nodes. Analogue of [calyx_ir::Seq] #[derive(Debug)] pub struct Seq(CtrlVec); impl Seq { + /// Create a new sequence operator from an iterator of control indices pub fn new(input: S) -> Self where S: Iterator, @@ -48,24 +52,28 @@ impl Seq { Self(input.collect()) } + /// Returns a reference to the control indices in the seq pub fn stms(&self) -> &[ControlIdx] { &self.0 } + /// Returns the number of control indices in the seq pub fn len(&self) -> usize { self.0.len() } + /// Returns whether the seq is empty pub fn is_empty(&self) -> bool { self.len() == 0 } } -/// Parallel compositions of control nodes +/// Parallel compositions of control nodes. Analogue of [calyx_ir::Par] #[derive(Debug)] pub struct Par(CtrlVec); impl Par { + /// Create a new par from an iterator of control indices pub fn new(input: S) -> Self where S: Iterator, @@ -73,20 +81,23 @@ impl Par { Self(input.collect()) } + /// Returns a reference to the body of the par pub fn stms(&self) -> &[ControlIdx] { &self.0 } + /// Returns the number of arms/threads in this par pub fn len(&self) -> usize { self.0.len() } + /// Returns true if this par contains no arms/threads pub fn is_empty(&self) -> bool { self.len() == 0 } } -/// An if-then-else control node +/// An if-then-else control node. Analogue of [calyx_ir::If] #[derive(Debug)] pub struct If { cond_port: PortRef, @@ -96,6 +107,7 @@ pub struct If { } impl If { + /// Construct a new if statement pub fn new( cond_port: PortRef, cond_group: Option, @@ -110,24 +122,28 @@ impl If { } } + /// Returns the port reference of the condition pub fn cond_port(&self) -> PortRef { self.cond_port } + /// Returns the index of the `with` group if it exists pub fn cond_group(&self) -> Option { self.cond_group } + /// Returns the index of the true branch pub fn tbranch(&self) -> ControlIdx { self.tbranch } + /// Returns the index of the false branch pub fn fbranch(&self) -> ControlIdx { self.fbranch } } -/// A while loop control node +/// A while loop control node. Analogue of [calyx_ir::While] #[derive(Debug)] pub struct While { cond_port: PortRef, @@ -136,6 +152,7 @@ pub struct While { } impl While { + /// Construct a new while node pub fn new( cond_port: PortRef, cond_group: Option, @@ -148,19 +165,23 @@ impl While { } } + /// Getter for the condition port pub fn cond_port(&self) -> PortRef { self.cond_port } + /// Getter for the condition group, if present pub fn cond_group(&self) -> Option { self.cond_group } + /// Getter for the loop body pub fn body(&self) -> ControlIdx { self.body } } +/// A container for the signature of an invocation command. #[derive(Debug)] pub struct InvokeSignature { /// The ports attached to the input of the invoked cell, an association list @@ -175,6 +196,8 @@ pub struct InvokeSignature { impl InvokeSignature { // TODO Griffin: fix this it's stupid + /// Returns an iterator over the ports in the signature. Ports are given as + /// (dest, src) pairs. pub fn iter(&self) -> impl Iterator { self.inputs .iter() @@ -184,7 +207,7 @@ impl InvokeSignature { } } -/// Invoke control node +/// Invoke control node. Analogue of [calyx_ir::Invoke] /// /// TODO Griffin: Consider making this smaller? Move ref_cells into signature box? #[derive(Debug)] @@ -198,15 +221,21 @@ pub struct Invoke { /// association list of the refcell offset in the invoked context, and the /// cell realizing it in the parent context pub ref_cells: SmallVec<[(LocalRefCellOffset, CellRef); 1]>, - /// The signature (behind a box for space reasons) + /// The signature (behind a box for space reasons). This is used during the + /// flattening process and printing, but is not used during simulation as + /// the assignments are pre-constructed from it. pub signature: Box, - + /// The go port pub go: PortRef, + /// The done port pub done: PortRef, + /// The assignments implied by the invocation command. These are + /// preconstructed during the flattening process. pub assignments: IndexRange, } impl Invoke { + /// Create a new invoke node pub fn new( cell: CellRef, comb_group: Option, @@ -236,32 +265,46 @@ impl Invoke { } } +/// A bounded loop #[derive(Debug)] pub struct Repeat { + /// The loop body pub body: ControlIdx, + /// The number of times to repeat the loop body pub num_repeats: u64, } impl Repeat { + /// Create a new bounded loop control node pub fn new(body: ControlIdx, num_repeats: u64) -> Self { Self { body, num_repeats } } } -/// An enum representing the different types of control nodes +/// An enum representing the different types of control nodes. Analogue of [calyx_ir::Control] #[derive(Debug)] pub enum ControlNode { + /// An empty control node Empty(Empty), + /// A group enable node Enable(Enable), + /// A sequential composition Seq(Seq), + /// A parallel composition Par(Par), + /// An if-then-else control node If(If), + /// A while loop control node While(While), + /// A bounded loop control node Repeat(Repeat), + /// An invoke control node Invoke(Invoke), } impl ControlNode { + /// Returns true if the control node is a leaf node (i.e. invoke, enable, or + /// empty) pub fn is_leaf(&self) -> bool { match self { ControlNode::While(_) diff --git a/interp/src/flatten/flat_ir/control/translator.rs b/interp/src/flatten/flat_ir/control/translator.rs index b7070dcfc0..842c7e27c0 100644 --- a/interp/src/flatten/flat_ir/control/translator.rs +++ b/interp/src/flatten/flat_ir/control/translator.rs @@ -6,7 +6,7 @@ use crate::{ as_raw::AsRaw, flatten::{ flat_ir::{ - cell_prototype::{CellPrototype, LiteralOrPrimitive}, + cell_prototype::{CellPrototype, ConstantType}, component::{AuxillaryComponentInfo, ComponentCore}, flatten_trait::{flatten_tree, FlattenTree, SingleHandle}, prelude::{ @@ -484,7 +484,7 @@ fn create_cell_prototype( let borrow = cell.borrow(); match &borrow.prototype { cir::CellType::Primitive { .. } => { - CellPrototype::construct_primitive(&borrow) + CellPrototype::construct_prototype(&borrow) } cir::CellType::Component { name } => { CellPrototype::Component(comp_id_map[name]) @@ -493,7 +493,7 @@ fn create_cell_prototype( cir::CellType::Constant { val, width } => CellPrototype::Constant { value: *val, width: (*width).try_into().unwrap(), - c_type: LiteralOrPrimitive::Literal, + c_type: ConstantType::Literal, }, cir::CellType::ThisComponent => unreachable!( "the flattening should not have this cell type, this is an error" diff --git a/interp/src/flatten/flat_ir/identifier.rs b/interp/src/flatten/flat_ir/identifier.rs index 82fe78d2f5..c20d15a1ae 100644 --- a/interp/src/flatten/flat_ir/identifier.rs +++ b/interp/src/flatten/flat_ir/identifier.rs @@ -3,6 +3,10 @@ use std::hash::Hash; use crate::flatten::structures::index_trait::{impl_index, IndexRef}; +/// An index type corresponding to a string. Similar to [calyx_ir::Id] but +/// cannot be turned into a string directly. Strings are stored in the +/// interpretation context [crate::flatten::structures::context::Context] and +/// can be looked up via [crate::flatten::structures::context::Context::lookup_name] #[derive(Debug, Eq, Copy, Clone, PartialEq, Hash, PartialOrd, Ord)] pub struct Identifier(u32); @@ -31,18 +35,28 @@ pub enum NameType { /// The standard port on a cell Cell, } - +/// The canonical way of representing a port definition. This is used to print values pub enum CanonicalIdentifier { + /// A normal port definition Standard { + /// The name of the port's parent parent: Identifier, + /// The name of the port name: Identifier, + /// The type of the parent name_type: NameType, }, /// A constant literal - Literal { width: u64, val: u64 }, + Literal { + /// The width of the literal + width: u64, + /// The value of the literal + val: u64, + }, } impl CanonicalIdentifier { + /// Creates a new interface port pub fn interface_port(parent: Identifier, name: Identifier) -> Self { Self::Standard { parent, @@ -51,6 +65,7 @@ impl CanonicalIdentifier { } } + /// Creates a new group hole port pub fn group_port(parent: Identifier, name: Identifier) -> Self { Self::Standard { parent, @@ -59,6 +74,7 @@ impl CanonicalIdentifier { } } + /// Creates a new cell port pub fn cell_port(parent: Identifier, name: Identifier) -> Self { Self::Standard { parent, @@ -67,10 +83,12 @@ impl CanonicalIdentifier { } } + /// Creates a new literal pub fn literal(width: u64, val: u64) -> Self { Self::Literal { width, val } } + /// Formats the name of the port pub fn format_name(&self, resolver: &IdMap) -> String { match self { CanonicalIdentifier::Standard { @@ -92,6 +110,7 @@ impl CanonicalIdentifier { } } + /// Returns the name of the port, if it is not a literal pub fn name(&self) -> Option<&Identifier> { match self { CanonicalIdentifier::Standard { name, .. } => name.into(), diff --git a/interp/src/flatten/flat_ir/mod.rs b/interp/src/flatten/flat_ir/mod.rs index e3b3b5a95a..837aaded3e 100644 --- a/interp/src/flatten/flat_ir/mod.rs +++ b/interp/src/flatten/flat_ir/mod.rs @@ -1,12 +1,21 @@ -pub mod attributes; +/// Core structures used by the rest of the IR pub mod base; +/// Cell prototypes (i.e cell types) pub mod cell_prototype; +/// Component definitions pub mod component; +/// Module collecting the structures related to the control section of a Calyx +/// program. pub mod control; -pub mod flatten_trait; +/// Module collecting the traits and structures related to the flattening +/// process. Internal and only used in the translator +pub(super) mod flatten_trait; +/// Module collecting structures around strings and identifiers pub mod identifier; +/// Module collecting the structures related to the wires section of a Calyx program pub mod wires; +/// Utility module with public imports for internal use pub(crate) mod prelude { pub use super::base::*; diff --git a/interp/src/flatten/flat_ir/wires/core.rs b/interp/src/flatten/flat_ir/wires/core.rs index 4d7c2c6440..b6f9cb8e5d 100644 --- a/interp/src/flatten/flat_ir/wires/core.rs +++ b/interp/src/flatten/flat_ir/wires/core.rs @@ -3,19 +3,29 @@ use crate::flatten::{ structures::{index_trait::IndexRange, indexed_map::IndexedMap}, }; +/// A map storing all the assignments defined by the program either explicitly +/// or implicitly pub type AssignmentMap = IndexedMap; +/// A map storing all the groups defined by the program pub type GroupMap = IndexedMap; +/// A map storing all the combinational groups defined by the program pub type CombGroupMap = IndexedMap; +/// An assignment in the program. Analogue of [calyx_ir::Assignment] #[derive(Debug)] pub struct Assignment { + /// The destination of the assignment pub dst: PortRef, + /// The source of the assignment pub src: PortRef, + /// The assignment's guard pub guard: GuardIdx, } +/// A group in the program. Analogue of [calyx_ir::Group] #[derive(Debug)] pub struct Group { + /// The name of the group name: Identifier, /// the assignments in this group pub assignments: IndexRange, @@ -26,6 +36,7 @@ pub struct Group { } impl Group { + /// Create a new group pub fn new( name: Identifier, assignments: IndexRange, @@ -40,11 +51,13 @@ impl Group { } } + /// Get the name of the group pub fn name(&self) -> Identifier { self.name } } +/// A combinational group in the program. Analogue of [calyx_ir::CombGroup] #[derive(Debug)] pub struct CombGroup { name: Identifier, @@ -53,6 +66,7 @@ pub struct CombGroup { } impl CombGroup { + /// Create a new combinational group pub fn new( name: Identifier, assignments: IndexRange, @@ -60,6 +74,7 @@ impl CombGroup { Self { name, assignments } } + /// Get the name of the group pub fn name(&self) -> Identifier { self.name } diff --git a/interp/src/flatten/flat_ir/wires/guards.rs b/interp/src/flatten/flat_ir/wires/guards.rs index d47052274e..9b3535714e 100644 --- a/interp/src/flatten/flat_ir/wires/guards.rs +++ b/interp/src/flatten/flat_ir/wires/guards.rs @@ -5,14 +5,25 @@ use crate::flatten::{ structures::indexed_map::IndexedMap, }; +/// A map storing all the guards defined in the program pub type GuardMap = IndexedMap; +/// A boolean expression that determines whether an assignment fires. Analogue +/// of [calyx_ir::Guard] #[derive(Debug)] pub enum Guard { + /// A guard that always fires. This is the default value for a guard and + /// applies when a guard is not explicitly defined. True, + /// A disjunction of two guards Or(GuardIdx, GuardIdx), + /// A conjunction of two guards And(GuardIdx, GuardIdx), + /// A negation of a guard Not(GuardIdx), + /// A guard that applies a comparison operator to two ports Comp(PortComp, PortRef, PortRef), + /// A guard that evaluates a given port as a boolean. In such cases, the + /// port must be a single bit. Port(PortRef), } diff --git a/interp/src/flatten/flat_ir/wires/mod.rs b/interp/src/flatten/flat_ir/wires/mod.rs index 9e51b91236..1b18f609ca 100644 --- a/interp/src/flatten/flat_ir/wires/mod.rs +++ b/interp/src/flatten/flat_ir/wires/mod.rs @@ -1,2 +1,4 @@ +/// A module containing the assignment and group definitions pub mod core; +/// A module containing the assignment guard definitions pub mod guards; diff --git a/interp/src/flatten/mod.rs b/interp/src/flatten/mod.rs index d4d3d9c735..1b3d7c7af0 100644 --- a/interp/src/flatten/mod.rs +++ b/interp/src/flatten/mod.rs @@ -1,3 +1,6 @@ +//! This module contains all the modules defining the core IR structures used +//! by the rest of Cider and the conversion from the Calyx IR. + pub mod flat_ir; pub mod primitives; pub mod structures; diff --git a/interp/src/flatten/primitives/builder.rs b/interp/src/flatten/primitives/builder.rs index a2f14aa4b5..f12d239792 100644 --- a/interp/src/flatten/primitives/builder.rs +++ b/interp/src/flatten/primitives/builder.rs @@ -4,7 +4,10 @@ use super::{combinational::*, stateful::*, Primitive}; use crate::{ flatten::{ flat_ir::{ - cell_prototype::{CellPrototype, FXType, MemType, SingleWidthType}, + cell_prototype::{ + CellPrototype, DoubleWidthType, FXType, MemType, + SingleWidthType, TripleWidthType, + }, prelude::{CellInfo, GlobalPortIdx}, }, structures::context::Context, @@ -100,13 +103,6 @@ pub fn build_primitive( Box::new(StdUndef::new(base_port, *width)) } }, - CellPrototype::BitSlice { - start_idx, - end_idx, - out_width, - } => Box::new(StdBitSlice::new( - base_port, *start_idx, *end_idx, *out_width, - )), CellPrototype::FixedPoint { op, width, @@ -138,21 +134,28 @@ pub fn build_primitive( Some(*frac_width), )), }, - CellPrototype::Slice { - in_width: _, // Not actually needed, should probably remove - out_width, - } => Box::new(StdSlice::new(base_port, *out_width)), - CellPrototype::Pad { - in_width: _, // Not actually needed, should probably remove - out_width, - } => Box::new(StdPad::new(base_port, *out_width)), - CellPrototype::Cat { - // Turns out under the assumption that the primitive is well formed, - // none of these parameter values are actually needed - left: _, - right: _, - out: _, - } => Box::new(StdCat::new(base_port)), + CellPrototype::DoubleWidth { op, width2, .. } => match op { + DoubleWidthType::Slice => { + Box::new(StdSlice::new(base_port, *width2)) + } + DoubleWidthType::Pad => Box::new(StdPad::new(base_port, *width2)), + }, + CellPrototype::TripleWidth { + op, + width1, + width2, + width3, + } => match op { + TripleWidthType::Cat => { + // Turns out under the assumption that the primitive is well formed, + // none of these parameter values are actually needed + Box::new(StdCat::new(base_port)) + } + TripleWidthType::BitSlice => { + Box::new(StdBitSlice::new(base_port, *width1, *width2, *width3)) + } + }, + CellPrototype::Memory { mem_type, width, diff --git a/interp/src/flatten/primitives/prim_trait.rs b/interp/src/flatten/primitives/prim_trait.rs index 2dc707ea7a..c89488548a 100644 --- a/interp/src/flatten/primitives/prim_trait.rs +++ b/interp/src/flatten/primitives/prim_trait.rs @@ -57,7 +57,7 @@ impl UpdateStatus { } #[inline] - /// Returns `true` if the update status is [`Changed`]. + /// Returns `true` if the update status is [`Changed`][]. /// /// [`Changed`]: UpdateStatus::Changed #[must_use] diff --git a/interp/src/flatten/structures/context.rs b/interp/src/flatten/structures/context.rs index b384995afc..374ffd5edd 100644 --- a/interp/src/flatten/structures/context.rs +++ b/interp/src/flatten/structures/context.rs @@ -90,15 +90,12 @@ impl Index for InterpretationContext { } } -impl InterpretationContext { - pub fn new() -> Self { - Default::default() - } -} - +/// Information about a port definition #[derive(Debug)] pub struct PortDefinitionInfo { + /// The name of the port pub name: Identifier, + /// The width of the port pub width: usize, } @@ -169,10 +166,7 @@ impl Index for SecondaryContext { } impl SecondaryContext { - pub fn new() -> Self { - Default::default() - } - + /// Insert a new local port definition into the context and return its index pub fn push_local_port( &mut self, name: Identifier, @@ -182,10 +176,12 @@ impl SecondaryContext { .push(PortDefinitionInfo { name, width }) } + /// Insert a new reference port definition into the context and return its index pub fn push_ref_port(&mut self, id: Identifier) -> RefPortDefinitionIdx { self.ref_port_defs.push(id) } + /// Insert a new local cell definition into the context and return its index pub fn push_local_cell( &mut self, name: Identifier, @@ -197,6 +193,7 @@ impl SecondaryContext { .push(CellInfo::new(name, ports, parent, prototype)) } + /// Insert a new reference cell definition into the context and return its index pub fn push_ref_cell( &mut self, name: Identifier, @@ -258,19 +255,24 @@ impl From<(InterpretationContext, SecondaryContext)> for Context { } impl Context { + /// Create a new empty context pub fn new() -> Self { Default::default() } + /// Resolve the string associated with the given identifier #[inline] pub fn resolve_id(&self, id: Identifier) -> &String { self.secondary.string_table.lookup_string(&id).unwrap() } + /// Create a new printer for the given context pub fn printer(&self) -> Printer { Printer::new(self) } + /// lookup the port definition for a port offset in a given component. This + /// will error is the offset is not valid. pub fn lookup_port_def( &self, comp: &ComponentIdx, @@ -280,6 +282,8 @@ impl Context { [self.secondary.comp_aux_info[*comp].port_offset_map[port]] } + /// Lookup the reference port definition for a port offset in a given + /// component. This will error is the offset is not valid. pub fn lookup_ref_port_def( &self, comp: &ComponentIdx, @@ -289,6 +293,8 @@ impl Context { [self.secondary.comp_aux_info[*comp].ref_port_offset_map[port]] } + /// Lookup the local cell definition for a cell offset in a given component. + /// This will error is the offset is not valid. pub fn lookup_cell_def( &self, comp: &ComponentIdx, @@ -298,6 +304,8 @@ impl Context { [self.secondary.comp_aux_info[*comp].cell_offset_map[cell]] } + /// Lookup the reference cell definition for a cell offset in a given + /// component. This will error is the offset is not valid. pub fn lookup_ref_cell_def( &self, comp: &ComponentIdx, @@ -323,6 +331,7 @@ impl Context { } } + /// Returns the component index with the given name, if such a component exists pub fn lookup_comp_by_name(&self, name: &str) -> Option { self.primary .components @@ -330,6 +339,7 @@ impl Context { .find(|c| self.resolve_id(self.secondary[*c].name) == name) } + /// Returns the group index with the given name within the given component, if such a group exists pub fn lookup_group_by_name( &self, name: &str, @@ -342,6 +352,7 @@ impl Context { .find(|x| self.resolve_id(self.primary[*x].name()) == name) } + /// Return the index of the component which defines the given group pub fn get_component_from_group(&self, group: GroupIdx) -> ComponentIdx { self.primary .components @@ -398,6 +409,8 @@ impl Context { } } + /// Lookup the name of the given object. This is used for definitions. For + /// instances, see [`Environment::get_full_name`](crate::flatten::structures::environment::Environment::get_full_name) pub fn lookup_name(&self, id: T) -> &String { id.lookup_name(self) } @@ -409,7 +422,10 @@ impl AsRef for &Context { } } +/// A trait for objects which have a name associated with them in the context. +/// This is used for definitions. pub trait LookupName { + /// Lookup the name of the object fn lookup_name<'ctx>(&self, ctx: &'ctx Context) -> &'ctx String; } diff --git a/interp/src/flatten/structures/environment/env.rs b/interp/src/flatten/structures/environment/env.rs index 44ed79bf56..98e3c96bea 100644 --- a/interp/src/flatten/structures/environment/env.rs +++ b/interp/src/flatten/structures/environment/env.rs @@ -256,7 +256,7 @@ impl PinnedPorts { #[derive(Debug)] pub struct Environment + Clone> { /// A map from global port IDs to their current values. - pub(crate) ports: PortMap, + ports: PortMap, /// A map from global cell IDs to their current state and execution info. pub(super) cells: CellMap, /// A map from global ref cell IDs to the cell they reference, if any. @@ -278,6 +278,9 @@ pub struct Environment + Clone> { } impl + Clone> Environment { + /// A utility function to get the context from the environment. This is not + /// suitable for cases in which mutation is required. In such cases, the + /// context should be accessed directly. pub fn ctx(&self) -> &Context { self.ctx.as_ref() } @@ -988,7 +991,7 @@ impl + Clone> Environment { cell: GlobalCellIdx, mut err: BoxedInterpreterError, ) -> BoxedInterpreterError { - let mut_err = err.into_inner(); + let mut_err = err.inner_mut(); match mut_err { InterpreterError::UndefinedWrite(s) diff --git a/interp/src/flatten/structures/printer.rs b/interp/src/flatten/structures/printer.rs index 8889f035e6..7d42a2bb71 100644 --- a/interp/src/flatten/structures/printer.rs +++ b/interp/src/flatten/structures/printer.rs @@ -1,7 +1,7 @@ use calyx_ir::PortComp; use crate::flatten::flat_ir::{ - cell_prototype::{CellPrototype, LiteralOrPrimitive}, + cell_prototype::{CellPrototype, ConstantType}, identifier::{CanonicalIdentifier, IdMap}, wires::guards::Guard, }; @@ -151,8 +151,8 @@ impl<'a> Printer<'a> { (PortDefinitionRef::Local(l), ParentIdx::Cell(c)) => { if let CellPrototype::Constant { value, width, c_type }= &self.ctx.secondary[c].prototype { match c_type { - LiteralOrPrimitive::Literal => CanonicalIdentifier::literal((*width).into(), *value), - LiteralOrPrimitive::Primitive => CanonicalIdentifier::cell_port( self.ctx.secondary[c].name, self.ctx.secondary[l].name), + ConstantType::Literal => CanonicalIdentifier::literal((*width).into(), *value), + ConstantType::Primitive => CanonicalIdentifier::cell_port( self.ctx.secondary[c].name, self.ctx.secondary[l].name), } } else { CanonicalIdentifier::cell_port( self.ctx.secondary[c].name, self.ctx.secondary[l].name) diff --git a/interp/src/main.rs b/interp/src/main.rs index ab989341a0..ce6a0dbf29 100644 --- a/interp/src/main.rs +++ b/interp/src/main.rs @@ -1,3 +1,5 @@ +//! Cider: The Calyx Interpreter and Debugger. + use argh::FromArgs; use calyx_utils::OutputFile;