diff --git a/multiply_add.sus b/multiply_add.sus index e75e847..9e5be2b 100644 --- a/multiply_add.sus +++ b/multiply_add.sus @@ -255,7 +255,7 @@ module blur : int a, bool done -> int result { state int prev; if working { - reg result = prev + a; // Add a pipeline stage for shits and giggles + reg reg reg result = prev + a; // Add a pipeline stage for shits and giggles } prev = a; working = !done; @@ -324,8 +324,10 @@ module first_bit_idx_24 : bool[24] bits -> int first { } } + module permute : bool[128] mbf, int selected_permutation -> bool[128] permuted_mbf { - // TODO + // cvt_to_double + permuted_mbf = mbf; } //timeline (X, [false;24], true -> /, false) | (X, vs, true -> X, true) .. (/, /, false -> X, true)* diff --git a/src/ast.rs b/src/ast.rs index 0a5fb36..f87eb2e 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -172,10 +172,6 @@ impl Module { for (id, inst) in &self.flattened.instantiations { println!(" {:?}: {:?}", id, inst); } - println!("Connections:"); - for conn in &self.flattened.connections { - println!(" {:?}", conn); - } } } diff --git a/src/flattening.rs b/src/flattening.rs index 33dd2a3..6158104 100644 --- a/src/flattening.rs +++ b/src/flattening.rs @@ -41,49 +41,33 @@ pub struct InterfacePort { pub id : FlatID } +#[derive(Debug)] +pub enum WireSource { + NamedWire{read_only : bool, identifier_type : IdentifierType, decl_id : Option}, + UnaryOp{op : Operator, right : SpanFlatID}, + BinaryOp{op : Operator, left : SpanFlatID, right : SpanFlatID}, + ArrayAccess{arr : SpanFlatID, arr_idx : SpanFlatID}, + Constant{value : Value}, +} + +#[derive(Debug)] +pub struct WireInstance { + pub typ : Type, + pub inst : WireSource +} + #[derive(Debug)] pub enum Instantiation { SubModule{module_uuid : NamedUUID, name : Box, typ_span : Span, interface_wires : Vec}, - PlainWire{read_only : bool, identifier_type : IdentifierType, typ : Type, decl_id : Option}, - UnaryOp{typ : Type, op : Operator, right : SpanFlatID}, - BinaryOp{typ : Type, op : Operator, left : SpanFlatID, right : SpanFlatID}, - ArrayAccess{typ : Type, arr : SpanFlatID, arr_idx : SpanFlatID}, - Constant{typ : Type, value : Value}, + Wire(WireInstance), Connection(Connection), Error } impl Instantiation { - pub fn get_type(&self) -> &Type { - match self { - Instantiation::SubModule{module_uuid : _, name : _, typ_span : _, interface_wires : _} => panic!("This is not a struct!"), - Instantiation::PlainWire{read_only: _, identifier_type : _, typ, decl_id : _} => typ, - Instantiation::UnaryOp{typ, op : _, right : _} => typ, - Instantiation::BinaryOp{typ, op : _, left : _, right : _} => typ, - Instantiation::ArrayAccess{typ, arr : _, arr_idx : _} => typ, - Instantiation::Constant{typ, value : _} => typ, - Instantiation::Connection(_) => panic!("Calling get_type on Connection!"), - Instantiation::Error => panic!("This was not properly resolved!") - } - } - - pub fn iter_sources ()>(&self, mut f : F) { - match self { - Instantiation::SubModule { module_uuid : _, name : _, typ_span : _, interface_wires } => { - for port in interface_wires { - if port.is_input { - f(port.id); - } - } - } - Instantiation::PlainWire { read_only : _, identifier_type : _, typ : _, decl_id : _ } => {} - Instantiation::UnaryOp { typ : _, op : _, right } => {f(right.0);} - Instantiation::BinaryOp { typ : _, op : _, left, right } => {f(left.0); f(right.0);} - Instantiation::ArrayAccess { typ : _, arr, arr_idx } => {f(arr.0); f(arr_idx.0)} - Instantiation::Constant { typ : _, value : _ } => {} - Instantiation::Connection(_) => panic!("Calling iter_sources on Connection!"), - Instantiation::Error => {} - } + pub fn extract_wire(&self) -> &WireInstance { + let Self::Wire(w) = self else {panic!("extract_wire on not a wire!")}; + w } } @@ -106,7 +90,7 @@ struct FlatteningContext<'l, 'm, 'fl> { impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { fn typecheck(&self, wire : SpanFlatID, expected : &Type, context : &str) -> Option<()> { - let found = self.instantiations[wire.0].get_type(); + let found = &self.instantiations[wire.0].extract_wire().typ; typecheck(found, wire.1, expected, context, self.linker, &self.errors) } pub fn map_to_type(&self, type_expr : &TypeExpression, global_references : &[GlobalReference]) -> Option { @@ -122,7 +106,7 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { } // May also error, for example when array accesses happen on non-array types fn get_connectionwrite_type(&self, cw : &ConnectionWrite) -> Option<&Type> { - let mut current_type = self.instantiations[cw.root].get_type(); + let mut current_type = &self.instantiations[cw.root].extract_wire().typ; for p in &cw.path { match p { ConnectionWritePathElement::ArrayIdx(idx) => { @@ -150,7 +134,7 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { } else { IdentifierType::Output }; - InterfacePort{is_input : port.is_input, id : self.instantiations.alloc(Instantiation::PlainWire { read_only : !port.is_input, identifier_type, typ: port.typ.clone(), decl_id : None })} + InterfacePort{is_input : port.is_input, id : self.instantiations.alloc(Instantiation::Wire(WireInstance{typ: port.typ.clone(), inst : WireSource::NamedWire { read_only : !port.is_input, identifier_type, decl_id : None }}))} }).collect(); Instantiation::SubModule{name, module_uuid, typ_span, interface_wires} @@ -217,17 +201,17 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { Expression::Named(LocalOrGlobal::Global(g)) => { let r = self.module.link_info.global_references[*g]; let cst = self.linker.try_get_constant(r, &self.errors)?; - self.instantiations.alloc(Instantiation::Constant{typ : cst.get_type(), value : cst}) + self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : cst.get_type(), inst : WireSource::Constant{value : cst}})) } Expression::Constant(cst) => { - self.instantiations.alloc(Instantiation::Constant{typ : cst.get_type(), value : cst.clone()}) + self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : cst.get_type(), inst : WireSource::Constant{value : cst.clone()}})) } Expression::UnaryOp(op_box) => { let (op, _op_pos, operate_on) = op_box.deref(); let right = self.flatten_single_expr(operate_on, condition)?; - let found = self.instantiations[right.0].get_type(); + let found = &self.instantiations[right.0].extract_wire().typ; let output_type = typecheck_unary_operator(*op, found, right.1, self.linker, &self.errors); - self.instantiations.alloc(Instantiation::UnaryOp{typ : output_type, op : *op, right}) + self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : output_type, inst : WireSource::UnaryOp{op : *op, right}})) } Expression::BinOp(binop_box) => { let (left_expr, op, _op_pos, right_expr) = binop_box.deref(); @@ -236,7 +220,7 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { let ((input_left_type, input_right_type), output_type) = get_binary_operator_types(*op); self.typecheck(left, &input_left_type, &format!("{op} left"))?; self.typecheck(right, &input_right_type, &format!("{op} right"))?; - self.instantiations.alloc(Instantiation::BinaryOp{typ : output_type, op : *op, left, right}) + self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : output_type, inst : WireSource::BinaryOp{op : *op, left, right}})) } Expression::Array(arr_box) => { let (left, right, bracket_span) = arr_box.deref(); @@ -244,10 +228,10 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { let arr_idx = self.flatten_single_expr(right, condition)?; let index_was_int = self.typecheck(arr_idx, &Type::Named(get_builtin_uuid("int")), "array index"); - let array_type = self.instantiations[arr.0].get_type(); + let array_type = &self.instantiations[arr.0].extract_wire().typ; let typ = typecheck_is_array_indexer(array_type, arr.1, self.linker, &self.errors)?.clone(); index_was_int?; // Do both for better typechecking diagnostics - self.instantiations.alloc(Instantiation::ArrayAccess{typ, arr, arr_idx}) + self.instantiations.alloc(Instantiation::Wire(WireInstance{typ, inst : WireSource::ArrayAccess{arr, arr_idx}})) } Expression::FuncCall(func_and_args) => { let (md, outputs) = self.desugar_func_call(func_and_args, expr_span.1, condition)?; @@ -267,7 +251,7 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { Some(match expr { AssignableExpression::Named{local_idx} => { let root = self.decl_to_flat_map[*local_idx]; - let Instantiation::PlainWire { read_only, identifier_type : _, typ : _, decl_id } = &self.instantiations[root] else { + let WireSource::NamedWire { read_only, identifier_type : _, decl_id } = &self.instantiations[root].extract_wire().inst else { unreachable!("Attempting to assign to a Instantiation::PlainWire") }; if *read_only { @@ -296,8 +280,8 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { additional_condition.0 } else { let bool_typ = Type::Named(get_builtin_uuid("bool")); - assert!(*self.instantiations[condition].get_type() == bool_typ); - self.instantiations.alloc(Instantiation::BinaryOp{typ : bool_typ, op: Operator{op_typ : kw("&")}, left : (condition, additional_condition.1), right : additional_condition}) + assert!(self.instantiations[condition].extract_wire().typ == bool_typ); + self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : bool_typ, inst : WireSource::BinaryOp{op: Operator{op_typ : kw("&")}, left : (condition, additional_condition.1), right : additional_condition}})) } } fn flatten_code(&mut self, code : &CodeBlock, condition : FlatID) { @@ -329,7 +313,7 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { Named::Type(_) => { assert!(decl.identifier_type != IdentifierType::Input); assert!(decl.identifier_type != IdentifierType::Output); - Instantiation::PlainWire{read_only : false, identifier_type : decl.identifier_type, typ, decl_id : Some(*decl_id)} + Instantiation::Wire(WireInstance{typ, inst : WireSource::NamedWire{read_only : false, identifier_type : decl.identifier_type, decl_id : Some(*decl_id)}}) } } }; @@ -344,7 +328,7 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { let then_condition = self.extend_condition(condition, if_statement_condition); self.flatten_code(then, then_condition); if let Some(e) = els { - let else_condition_bool = (self.instantiations.alloc(Instantiation::UnaryOp{typ : bool_typ, op : Operator{op_typ : kw("!")}, right : if_statement_condition}), condition_expr.1); + let else_condition_bool = (self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : bool_typ, inst : WireSource::UnaryOp{op : Operator{op_typ : kw("!")}, right : if_statement_condition}})), condition_expr.1); let else_condition = self.extend_condition(condition, else_condition_bool); self.flatten_code(e, else_condition); } @@ -435,7 +419,6 @@ impl FlattenedInterface { #[derive(Debug)] pub struct FlattenedModule { pub instantiations : ListAllocator, - pub connections : BlockVec, pub errors : ErrorCollector } @@ -443,7 +426,6 @@ impl FlattenedModule { pub fn empty(file : FileUUID) -> FlattenedModule { FlattenedModule { instantiations : ListAllocator::new(), - connections : BlockVec::new(), errors : ErrorCollector::new(file) } } @@ -457,7 +439,6 @@ impl FlattenedModule { let flat_mod = FlattenedModule { instantiations: ListAllocator::new(), - connections: BlockVec::new(), errors: ErrorCollector::new(module.link_info.file) }; @@ -477,7 +458,7 @@ impl FlattenedModule { }; let (wire_id, typ) = if let Some(typ) = context.map_to_type(&decl.typ.0, &module.link_info.global_references) { - let wire_id = context.instantiations.alloc(Instantiation::PlainWire { read_only: is_input, identifier_type : decl.identifier_type, typ : typ.clone(), decl_id : Some(decl_id)}); + let wire_id = context.instantiations.alloc(Instantiation::Wire(WireInstance{typ : typ.clone(), inst : WireSource::NamedWire{read_only: is_input, identifier_type : decl.identifier_type, decl_id : Some(decl_id)}})); (wire_id, typ) } else { (UUID::INVALID, Type::Named(UUID::INVALID)) @@ -511,10 +492,12 @@ impl FlattenedModule { // Setup Wire Fanouts List for faster processing let mut connection_fanin : FlatAlloc, FlatIDMarker> = self.instantiations.iter().map(|_| Vec::new()).collect(); - for conn in &self.connections { - connection_fanin[conn.to.root].push(conn.from.0); - if conn.condition != UUID::INVALID { - connection_fanin[conn.to.root].push(conn.condition); + for (id, conn) in &self.instantiations { + if let Instantiation::Connection(conn) = conn { + connection_fanin[conn.to.root].push(conn.from.0); + if conn.condition != UUID::INVALID { + connection_fanin[conn.to.root].push(conn.condition); + } } } @@ -535,13 +518,32 @@ impl FlattenedModule { println!("{:?}", wire_to_explore_queue); while let Some(item) = wire_to_explore_queue.pop() { - let wire = &self.instantiations[item]; - wire.iter_sources(|from| { + let mut func = |from| { if !is_instance_used_map[from] { is_instance_used_map[from] = true; wire_to_explore_queue.push(from); } - }); + }; + match &self.instantiations[item] { + Instantiation::Wire(wire) => { + match &wire.inst { + WireSource::NamedWire{read_only : _, identifier_type : _, decl_id : _} => {} + WireSource::UnaryOp{op : _, right} => {func(right.0);} + WireSource::BinaryOp{op : _, left, right} => {func(left.0); func(right.0);} + WireSource::ArrayAccess{arr, arr_idx} => {func(arr.0); func(arr_idx.0)} + WireSource::Constant{value : _} => {} + } + } + Instantiation::SubModule{module_uuid, name, typ_span, interface_wires} => { + for port in interface_wires { + if port.is_input { + func(port.id); + } + } + } + Instantiation::Connection(_) => {unreachable!()} + Instantiation::Error => {} + } for from in &connection_fanin[item] { if !is_instance_used_map[*from] { is_instance_used_map[*from] = true; @@ -550,15 +552,10 @@ impl FlattenedModule { } } - println!("Final"); - println!("{:?}", connection_fanin); - println!("{:?}", is_instance_used_map); - println!("{:?}", wire_to_explore_queue); - // Now produce warnings from the unused list for (id, inst) in &self.instantiations { if !is_instance_used_map[id] { - if let Instantiation::PlainWire { read_only : _, identifier_type, typ : _, decl_id : Some(decl_id) } = inst { + if let Instantiation::Wire(WireInstance{typ : _, inst : WireSource::NamedWire { read_only : _, identifier_type, decl_id : Some(decl_id) }}) = inst { self.errors.warn_basic(Span::from(md.declarations[*decl_id].name_token), "Unused Variable: This variable does not affect the output ports of this module"); } } diff --git a/src/instantiation/mod.rs b/src/instantiation/mod.rs index d0b1277..e0799e8 100644 --- a/src/instantiation/mod.rs +++ b/src/instantiation/mod.rs @@ -2,7 +2,7 @@ use std::{rc::Rc, ops::Deref, cell::RefCell}; use num::{BigUint, FromPrimitive}; -use crate::{arena_alloc::{UUID, UUIDMarker, FlatAlloc}, ast::{Value, Operator, Module, IdentifierType}, typing::{ConcreteType, Type}, flattening::{FlatID, Instantiation, FlatIDMarker, ConnectionWrite, ConnectionWritePathElement}, errors::ErrorCollector, linker::{Linker, get_builtin_uuid}}; +use crate::{arena_alloc::{UUID, UUIDMarker, FlatAlloc}, ast::{Value, Operator, Module, IdentifierType}, typing::{ConcreteType, Type}, flattening::{FlatID, Instantiation, FlatIDMarker, ConnectionWrite, ConnectionWritePathElement, WireSource}, errors::ErrorCollector, linker::{Linker, get_builtin_uuid}}; pub mod latency; @@ -139,7 +139,7 @@ struct InstantiationContext<'fl, 'l> { impl<'fl, 'l> InstantiationContext<'fl, 'l> { fn compute_constant(&self, wire : FlatID) -> Value { - if let Instantiation::Constant { typ : _, value } = &self.module.flattened.instantiations[wire] { + if let WireSource::Constant{value} = &self.module.flattened.instantiations[wire].extract_wire().inst { value.clone() } else { println!("TODO HIT"); @@ -150,14 +150,14 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> { match typ { Type::Named(n) => { ConcreteType::Named(*n) - }, + } Type::Array(arr_box) => { let (arr_content_typ, arr_size_wire) = arr_box.deref(); let inner_typ = self.concretize_type(arr_content_typ); let Value::Integer(v) = self.compute_constant(*arr_size_wire) else {panic!("Not an int, should have been solved beforehand!")}; - let arr_usize = u64::try_from(v).expect("Array size should be small enough"); + let arr_usize = u64::try_from(v).expect("Array size cannot exceed u64::MAX"); ConcreteType::Array(Box::new((inner_typ, arr_usize))) - }, + } } } fn process_connection(&mut self, to : &ConnectionWrite, from : ConnectFrom) { @@ -189,11 +189,6 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> { sources.push(MultiplexerSource{from, path : new_path}) } - fn add_wire(&mut self, name : Option>, typ : &Type, original_wire : FlatID, source : RealWireDataSource) -> SubModuleOrWire { - let name = name.unwrap_or_else(|| {format!("_{}", self.wires.get_next_alloc_id().get_hidden_value()).into_boxed_str()}); - SubModuleOrWire::Wire(self.wires.alloc(RealWire{ name, latency : i64::MIN /* Invalid */, typ : self.concretize_type(typ), original_wire, source})) - } - fn instantiate_flattened_module(&mut self) { for (original_wire, inst) in &self.module.flattened.instantiations { let instance_to_add : SubModuleOrWire = match inst { @@ -204,27 +199,33 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> { }).collect(); SubModuleOrWire::SubModule(self.submodules.alloc(SubModule { original_flat: original_wire, instance, wires : interface_real_wires, name : name.clone()})) } - Instantiation::PlainWire{read_only, identifier_type, typ, decl_id} => { - let source = if *read_only { - RealWireDataSource::ReadOnly - } else { - // TODO initial value - let is_state = if *identifier_type == IdentifierType::State {StateInitialValue::State { initial_value: None }} else {StateInitialValue::NotState}; - RealWireDataSource::Multiplexer {is_state, sources : Vec::new()} + Instantiation::Wire(w) => { + let (name, source) = match &w.inst { + WireSource::NamedWire{read_only, identifier_type, decl_id} => { + let source = if *read_only { + RealWireDataSource::ReadOnly + } else { + // TODO initial value + let is_state = if *identifier_type == IdentifierType::State {StateInitialValue::State { initial_value: None }} else {StateInitialValue::NotState}; + RealWireDataSource::Multiplexer {is_state, sources : Vec::new()} + }; + (decl_id.map(|id| self.module.declarations[id].name.clone()), source) + } + WireSource::UnaryOp{op, right} => { + (None, RealWireDataSource::UnaryOp{op: *op, right: self.instance_map[right.0].extract_wire() }) + } + WireSource::BinaryOp{op, left, right} => { + (None, RealWireDataSource::BinaryOp{op: *op, left: self.instance_map[left.0].extract_wire(), right: self.instance_map[right.0].extract_wire() }) + } + WireSource::ArrayAccess{arr, arr_idx} => { + (None, RealWireDataSource::ArrayAccess{arr: self.instance_map[arr.0].extract_wire(), arr_idx: self.instance_map[arr_idx.0].extract_wire() }) + } + WireSource::Constant{value} => { + (None, RealWireDataSource::Constant{value : value.clone() }) + } }; - self.add_wire(decl_id.map(|id| self.module.declarations[id].name.clone()), typ, original_wire, source) - } - Instantiation::UnaryOp{typ, op, right} => { - self.add_wire(None, typ, original_wire, RealWireDataSource::UnaryOp{op: *op, right: self.instance_map[right.0].extract_wire() }) - } - Instantiation::BinaryOp{typ, op, left, right} => { - self.add_wire(None, typ, original_wire, RealWireDataSource::BinaryOp{op: *op, left: self.instance_map[left.0].extract_wire(), right: self.instance_map[right.0].extract_wire() }) - } - Instantiation::ArrayAccess{typ, arr, arr_idx} => { - self.add_wire(None, typ, original_wire, RealWireDataSource::ArrayAccess{arr: self.instance_map[arr.0].extract_wire(), arr_idx: self.instance_map[arr_idx.0].extract_wire() }) - } - Instantiation::Constant{typ, value} => { - self.add_wire(None, typ, original_wire, RealWireDataSource::Constant{value : value.clone() }) + let name = name.unwrap_or_else(|| {format!("_{}", self.wires.get_next_alloc_id().get_hidden_value()).into_boxed_str()}); + SubModuleOrWire::Wire(self.wires.alloc(RealWire{ name, latency : i64::MIN /* Invalid */, typ : self.concretize_type(&w.typ), original_wire, source})) } Instantiation::Connection(conn) => { let condition = if conn.condition != UUID::INVALID { diff --git a/src/typing.rs b/src/typing.rs index 760cebf..5901a9b 100644 --- a/src/typing.rs +++ b/src/typing.rs @@ -3,7 +3,7 @@ use std::ops::Deref; use crate::{ast::{Value, Operator, Span}, linker::{get_builtin_uuid, NamedUUID, Linker, Linkable}, tokenizer::kw, flattening::FlatID, errors::ErrorCollector, arena_alloc::UUID}; // Types contain everything that cannot be expressed at runtime -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone)] pub enum Type { Named(NamedUUID), /*Contains a wireID pointing to a constant expression for the array size, @@ -13,6 +13,17 @@ pub enum Type { Array(Box<(Type, FlatID)>) } +impl PartialEq for Type { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Named(l0), Self::Named(r0)) => l0 == r0, + (Self::Array(l0), Self::Array(r0)) => l0.deref().0 == r0.deref().0, + _ => false, + } + } +} +impl Eq for Type {} + impl Type { pub fn to_string(&self, linker : &Linker) -> String { match self { @@ -96,6 +107,7 @@ pub fn typecheck(found : &Type, span : Span, expected : &Type, context : &str, l let expected_name = expected.to_string(linker); let found_name = found.to_string(linker); errors.error_basic(span, format!("Typing Error: {context} expects a {expected_name} but was given a {found_name}")); + assert!(expected_name != found_name); None } else { Some(())