From 5fc7d625f64aa5fb28f7c2f7c56414916b3513fc Mon Sep 17 00:00:00 2001 From: Lennart Van Hirtum Date: Wed, 3 Jan 2024 18:32:11 +0100 Subject: [PATCH] Refactor to use Instantiation-Associated spans --- src/flattening.rs | 76 ++++++++++++++++++++-------------------- src/instantiation/mod.rs | 76 +++++++++++++++++++++------------------- 2 files changed, 77 insertions(+), 75 deletions(-) diff --git a/src/flattening.rs b/src/flattening.rs index 9e265b6..d7eb858 100644 --- a/src/flattening.rs +++ b/src/flattening.rs @@ -11,13 +11,11 @@ pub struct FlatIDMarker; impl UUIDMarker for FlatIDMarker {const DISPLAY_NAME : &'static str = "obj_";} pub type FlatID = UUID; -pub type SpanFlatID = (FlatID, Span); - pub type FieldID = usize; #[derive(Debug)] pub enum ConnectionWritePathElement { - ArrayIdx(SpanFlatID), + ArrayIdx{idx : FlatID, idx_span : Span}, //StructField(FieldID) } #[derive(Debug)] @@ -42,7 +40,7 @@ impl ConnectionWrite { #[derive(Debug)] pub struct Connection { pub num_regs : i64, - pub from : SpanFlatID, + pub from : FlatID, pub to : ConnectionWrite, pub condition : Option } @@ -57,9 +55,9 @@ pub struct InterfacePort { pub enum WireSource { NamedWire{read_only : bool, identifier_type : IdentifierType, name : Box, name_token : Option}, WireRead{from_wire : FlatID}, // Used to add a span to the reference of a wire. - UnaryOp{op : Operator, right : SpanFlatID}, - BinaryOp{op : Operator, left : SpanFlatID, right : SpanFlatID}, - ArrayAccess{arr : SpanFlatID, arr_idx : SpanFlatID}, + UnaryOp{op : Operator, right : FlatID}, + BinaryOp{op : Operator, left : FlatID, right : FlatID}, + ArrayAccess{arr : FlatID, arr_idx : FlatID}, Constant{value : Value}, } @@ -96,9 +94,9 @@ 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].extract_wire().typ; - typecheck(found, wire.1, expected, context, self.linker, &self.errors) + fn typecheck(&self, wire_id : FlatID, expected : &Type, context : &str) -> Option<()> { + let wire = self.instantiations[wire_id].extract_wire(); + typecheck(&wire.typ, wire.span, expected, context, self.linker, &self.errors) } pub fn map_to_type(&self, type_expr : &TypeExpression, global_references : &[GlobalReference]) -> Type { match type_expr { @@ -114,7 +112,7 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { let array_element_type = self.map_to_type(&array_type_expr.0, global_references); if let Some(array_size_wire) = self.flatten_single_expr(array_size_expr, None) { self.typecheck(array_size_wire, &Type::Named(get_builtin_uuid("int")), "array size"); - Type::Array(Box::new((array_element_type, array_size_wire.0))) + Type::Array(Box::new((array_element_type, array_size_wire))) } else { Type::Error } @@ -126,9 +124,9 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { let mut current_type = &self.instantiations[cw.root].extract_wire().typ; for p in &cw.path { match p { - ConnectionWritePathElement::ArrayIdx(idx) => { + ConnectionWritePathElement::ArrayIdx{idx, idx_span} => { let index_was_int = self.typecheck(*idx, &Type::Named(get_builtin_uuid("int")), "array index"); - current_type = typecheck_is_array_indexer(current_type, idx.1, self.linker, &self.errors)?; + current_type = typecheck_is_array_indexer(current_type, *idx_span, self.linker, &self.errors)?; index_was_int?; } } @@ -141,9 +139,10 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { let did_typecheck_fail = self.typecheck(connection.from, &expected_type, "connection"); let assign_to_root = self.instantiations[connection.to.root].extract_wire(); - if assign_to_root.is_compiletime && !self.instantiations[connection.from.0].extract_wire().is_compiletime { + let from_wire = self.instantiations[connection.from].extract_wire(); + if assign_to_root.is_compiletime && !from_wire.is_compiletime { let decl_info = error_info(assign_to_root.span, self.errors.file, "Declared here"); - self.errors.error_with_info(connection.from.1, "Assignments to compile-time variables must themselves be known at compile time", vec![decl_info]); + self.errors.error_with_info(from_wire.span, "Assignments to compile-time variables must themselves be known at compile time", vec![decl_info]); return None; } did_typecheck_fail?;// gather all errors, then @@ -227,7 +226,7 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { Some((md, &interface_wires[output_range])) } - fn flatten_single_expr(&self, (expr, expr_span) : &SpanExpression, condition : Option) -> Option { + fn flatten_single_expr(&self, (expr, expr_span) : &SpanExpression, condition : Option) -> Option { let span = *expr_span; // for more compact constructors let single_connection_side = match expr { Expression::Named(LocalOrGlobal::Local(l)) => { @@ -246,9 +245,9 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { 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].extract_wire(); - let output_type = typecheck_unary_operator(*op, &found.typ, right.1, self.linker, &self.errors); - self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : output_type, is_compiletime : found.is_compiletime, span, inst : WireSource::UnaryOp{op : *op, right}})) + let right_wire = self.instantiations[right].extract_wire(); + let output_type = typecheck_unary_operator(*op, &right_wire.typ, right_wire.span, self.linker, &self.errors); + self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : output_type, is_compiletime : right_wire.is_compiletime, span, inst : WireSource::UnaryOp{op : *op, right}})) } Expression::BinOp(binop_box) => { let (left_expr, op, _op_pos, right_expr) = binop_box.deref(); @@ -257,7 +256,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"))?; - let is_constant = self.instantiations[left.0].extract_wire().is_compiletime && self.instantiations[right.0].extract_wire().is_compiletime; + let is_constant = self.instantiations[left].extract_wire().is_compiletime && self.instantiations[right].extract_wire().is_compiletime; self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : output_type, is_compiletime: is_constant, span, inst : WireSource::BinaryOp{op : *op, left, right}})) } Expression::Array(arr_box) => { @@ -266,11 +265,11 @@ 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].extract_wire().typ; - let typ = typecheck_is_array_indexer(array_type, arr.1, self.linker, &self.errors)?.clone(); + let arr_wire = self.instantiations[arr].extract_wire(); + let arr_idx_wire = self.instantiations[arr_idx].extract_wire(); + let typ = typecheck_is_array_indexer(&arr_wire.typ, arr_wire.span, self.linker, &self.errors)?.clone(); index_was_int?; // Do both for better typechecking diagnostics - let idx_is_constant = self.instantiations[arr_idx.0].extract_wire().is_compiletime; - let is_constant = self.instantiations[arr.0].extract_wire().is_compiletime && idx_is_constant; + let is_constant = arr_wire.is_compiletime && arr_idx_wire.is_compiletime; self.instantiations.alloc(Instantiation::Wire(WireInstance{typ, is_compiletime: is_constant, span, inst : WireSource::ArrayAccess{arr, arr_idx}})) } Expression::FuncCall(func_and_args) => { @@ -285,7 +284,7 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { outputs[0].id } }; - Some((single_connection_side, *expr_span)) + Some(single_connection_side) } fn flatten_assignable_expr(&self, (expr, span) : &SpanAssignableExpression, condition : Option) -> Option { Some(match expr { @@ -302,28 +301,29 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { ConnectionWrite{root, path : Vec::new(), span : *span} } AssignableExpression::ArrayIndex(arr_box) => { - let (arr, idx, _bracket_span) = arr_box.deref(); + let (arr, idx_expr, _bracket_span) = arr_box.deref(); let flattened_arr_expr_opt = self.flatten_assignable_expr(arr, condition); - let idx_local = self.flatten_single_expr(idx, condition)?; + let idx = self.flatten_single_expr(idx_expr, condition)?; let mut flattened_arr_expr = flattened_arr_expr_opt?; // only unpack the subexpr after flattening the idx, so we catch all errors - flattened_arr_expr.path.push(ConnectionWritePathElement::ArrayIdx(idx_local)); + flattened_arr_expr.path.push(ConnectionWritePathElement::ArrayIdx{idx, idx_span : idx_expr.1}); flattened_arr_expr } }) } - fn extend_condition(&self, condition : Option, additional_condition : SpanFlatID) -> FlatID { + fn extend_condition(&self, condition : Option, additional_condition : FlatID) -> FlatID { if let Some(condition) = condition { let bool_typ = Type::Named(get_builtin_uuid("bool")); - let prev_cond = self.instantiations[condition].extract_wire(); - assert!(!prev_cond.is_compiletime); // Conditions are only used for runtime conditions. Compile time ifs are handled at instantiation time - assert!(prev_cond.typ == bool_typ); - self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : bool_typ, is_compiletime : false, span : additional_condition.1, inst : WireSource::BinaryOp{op: Operator{op_typ : kw("&")}, left : (condition, additional_condition.1), right : additional_condition}})) + let prev_condition_wire = self.instantiations[condition].extract_wire(); + let additional_condition_wire = self.instantiations[condition].extract_wire(); + assert!(!prev_condition_wire.is_compiletime); // Conditions are only used for runtime conditions. Compile time ifs are handled at instantiation time + assert!(prev_condition_wire.typ == bool_typ); + self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : bool_typ, is_compiletime : false, span : additional_condition_wire.span, inst : WireSource::BinaryOp{op: Operator{op_typ : kw("&")}, left : condition, right : additional_condition}})) } else { - additional_condition.0 + additional_condition } } fn flatten_code(&mut self, code : &CodeBlock, condition : Option) { @@ -366,7 +366,7 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { Statement::If{condition : condition_expr, then, els} => { let Some(if_statement_condition) = self.flatten_single_expr(condition_expr, condition) else {continue;}; - let condition_is_const = self.instantiations[if_statement_condition.0].extract_wire().is_compiletime; + let condition_is_const = self.instantiations[if_statement_condition].extract_wire().is_compiletime; if condition_is_const { println!("TODO generative if statements"); @@ -377,7 +377,7 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { let then_condition = self.extend_condition(condition, if_statement_condition); self.flatten_code(then, Some(then_condition)); if let Some(e) = els { - let else_condition_bool = (self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : bool_typ, is_compiletime : false/* Generative If */, span : condition_expr.1, inst : WireSource::UnaryOp{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, is_compiletime : false/* Generative If */, span : condition_expr.1, inst : WireSource::UnaryOp{op : Operator{op_typ : kw("!")}, right : if_statement_condition}})); let else_condition = self.extend_condition(condition, else_condition_bool); self.flatten_code(e, Some(else_condition)); } @@ -401,7 +401,7 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { for (field, to_i) in zip(outputs, to) { let Some(write_side) = self.flatten_assignable_expr(&to_i.expr, condition) else {return;}; - self.create_connection(Connection{num_regs : to_i.num_regs, from: (field.id, func_name_span), to: write_side, condition}); + self.create_connection(Connection{num_regs : to_i.num_regs, from: field.id, to: write_side, condition}); } }, Statement::Assign{to, expr : non_func_expr, eq_sign_position : _} => { @@ -540,7 +540,7 @@ impl FlattenedModule { for (_id, conn) in &self.instantiations { if let Instantiation::Connection(conn) = conn { - connection_fanin[conn.to.root].push(conn.from.0); + connection_fanin[conn.to.root].push(conn.from); if let Some(cond) = conn.condition { connection_fanin[conn.to.root].push(cond); } diff --git a/src/instantiation/mod.rs b/src/instantiation/mod.rs index 27a54ba..a3cfc17 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::BigInt; -use crate::{arena_alloc::{UUID, UUIDMarker, FlatAlloc}, ast::{Operator, Module, IdentifierType, Span}, typing::{ConcreteType, Type}, flattening::{FlatID, Instantiation, FlatIDMarker, ConnectionWritePathElement, WireSource, SpanFlatID, WireInstance, Connection, ConnectionWritePathElementComputed}, errors::ErrorCollector, linker::{Linker, get_builtin_uuid}, value::{Value, compute_unary_op, compute_binary_op}}; +use crate::{arena_alloc::{UUID, UUIDMarker, FlatAlloc}, ast::{Operator, Module, IdentifierType, Span}, typing::{ConcreteType, Type}, flattening::{FlatID, Instantiation, FlatIDMarker, ConnectionWritePathElement, WireSource, WireInstance, Connection, ConnectionWritePathElementComputed}, errors::ErrorCollector, linker::{Linker, get_builtin_uuid}, value::{Value, compute_unary_op, compute_binary_op}}; pub mod latency; @@ -198,8 +198,8 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> { for pe in to_path { match pe { - ConnectionWritePathElement::ArrayIdx(arr_idx) => { - match &self.generation_state[arr_idx.0] { + ConnectionWritePathElement::ArrayIdx{idx, idx_span} => { + match &self.generation_state[*idx] { SubModuleOrWire::SubModule(_) => unreachable!(), SubModuleOrWire::Unnasigned => unreachable!(), SubModuleOrWire::Wire(idx_wire) => { @@ -208,7 +208,7 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> { new_path.push(ConnectToPathElem::MuxArrayWrite{idx_wire : *idx_wire}); } SubModuleOrWire::CompileTimeValue(v) => { - let Some(idx) = self.extract_integer_from_value(v, arr_idx.1) else {return}; + let Some(idx) = self.extract_integer_from_value(v, *idx_span) else {return}; new_path.push(ConnectToPathElem::ConstArrayWrite{idx}); } } @@ -232,9 +232,9 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> { result.reserve(conn_path.len()); for p in conn_path { match p { - ConnectionWritePathElement::ArrayIdx(idx) => { - let Some(idx_val) = self.get_generation_value(idx.0) else {return None}; - let Some(idx_val) = self.extract_integer_from_value::(idx_val, idx.1) else {return None}; + ConnectionWritePathElement::ArrayIdx{idx, idx_span} => { + let Some(idx_val) = self.get_generation_value(*idx) else {return None}; + let Some(idx_val) = self.extract_integer_from_value::(idx_val, *idx_span) else {return None}; result.push(ConnectionWritePathElementComputed::ArrayIdx(idx_val)) } } @@ -250,7 +250,7 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> { let condition = conn.condition.map(|found_conn| self.generation_state[found_conn].extract_wire()); - let Some(from) = self.get_wire_or_constant_as_wire(&conn.from) else {return;}; + let Some(from) = self.get_wire_or_constant_as_wire(conn.from) else {return;}; let conn_from = ConnectFrom { num_regs: conn.num_regs, from, @@ -263,7 +263,7 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> { return; } SubModuleOrWire::CompileTimeValue(_original_value) => { // Compiletime wire - let found_v = self.generation_state[conn.from.0].extract_generation_value().clone(); + let found_v = self.generation_state[conn.from].extract_generation_value().clone(); let Some(cvt_path) = self.convert_connection_path_to_known_values(&conn.to.path) else {return}; // Hack to get around the borrow rules here let SubModuleOrWire::CompileTimeValue(v_writable) = &mut self.generation_state[conn.to.root] else {unreachable!()}; @@ -289,26 +289,27 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> { } typ.get_initial_val(self.linker) } - WireSource::WireRead{from_wire} => { - self.get_generation_value(*from_wire)?.clone() + &WireSource::WireRead{from_wire} => { + self.get_generation_value(from_wire)?.clone() } - WireSource::UnaryOp{op, right} => { - let right_val = self.get_generation_value(right.0)?; - compute_unary_op(*op, right_val) + &WireSource::UnaryOp{op, right} => { + let right_val = self.get_generation_value(right)?; + compute_unary_op(op, right_val) } - WireSource::BinaryOp{op, left, right} => { - let left_val = self.get_generation_value(left.0)?; - let right_val = self.get_generation_value(right.0)?; - compute_binary_op(left_val, *op, right_val) + &WireSource::BinaryOp{op, left, right} => { + let left_val = self.get_generation_value(left)?; + let right_val = self.get_generation_value(right)?; + compute_binary_op(left_val, op, right_val) } - WireSource::ArrayAccess{arr, arr_idx} => { - let Value::Array(arr_val) = self.get_generation_value(arr.0)? else {return None}; - let arr_idx_val = self.get_generation_value(arr_idx.0)?; - let idx : usize = self.extract_integer_from_value(arr_idx_val, arr_idx.1)?; + &WireSource::ArrayAccess{arr, arr_idx} => { + let Value::Array(arr_val) = self.get_generation_value(arr)? else {return None}; + let arr_idx_val = self.get_generation_value(arr_idx)?; + let arr_idx_wire = self.module.flattened.instantiations[arr_idx].extract_wire(); + let idx : usize = self.extract_integer_from_value(arr_idx_val, arr_idx_wire.span)?; if let Some(item) = arr_val.get(idx) { item.clone() } else { - self.errors.error_basic(arr_idx.1, format!("Compile-Time Array index is out of range: idx: {idx}, array size: {}", arr_val.len())); + self.errors.error_basic(arr_idx_wire.span, format!("Compile-Time Array index is out of range: idx: {idx}, array size: {}", arr_val.len())); return None } } @@ -318,17 +319,17 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> { fn get_unique_name(&self) -> Box { format!("_{}", self.wires.get_next_alloc_id().get_hidden_value()).into_boxed_str() } - fn get_wire_or_constant_as_wire(&mut self, flat_id : &SpanFlatID) -> Option { - match &self.generation_state[flat_id.0] { + fn get_wire_or_constant_as_wire(&mut self, flat_id : FlatID) -> Option { + match &self.generation_state[flat_id] { SubModuleOrWire::SubModule(_) => unreachable!(), SubModuleOrWire::Unnasigned => unreachable!(), SubModuleOrWire::Wire(w) => Some(*w), SubModuleOrWire::CompileTimeValue(v) => { let value = v.clone(); - let Instantiation::Wire(WireInstance{typ, inst : _, is_compiletime : _, span}) = &self.module.flattened.instantiations[flat_id.0] else {unreachable!()}; + let Instantiation::Wire(WireInstance{typ, inst : _, is_compiletime : _, span}) = &self.module.flattened.instantiations[flat_id] else {unreachable!()}; let typ = self.concretize_type(typ, *span)?; let name = self.get_unique_name(); - Some(self.wires.alloc(RealWire{source : RealWireDataSource::Constant{value}, original_wire : flat_id.0, typ, name})) + Some(self.wires.alloc(RealWire{source : RealWireDataSource::Constant{value}, original_wire : flat_id, typ, name})) } } } @@ -344,29 +345,30 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> { }; (Some(name.clone()), source) } - WireSource::WireRead{from_wire} => { - let WireSource::NamedWire { read_only, identifier_type, name, name_token } = &self.module.flattened.instantiations[*from_wire].extract_wire().inst else {unreachable!("WireReads must point to a NamedWire!")}; - return Some(self.generation_state[*from_wire].extract_wire()) + &WireSource::WireRead{from_wire} => { + let WireSource::NamedWire{read_only:_, identifier_type:_, name:_, name_token:_} = &self.module.flattened.instantiations[from_wire].extract_wire().inst else {unreachable!("WireReads must point to a NamedWire!")}; + return Some(self.generation_state[from_wire].extract_wire()) } - WireSource::UnaryOp{op, right} => { + &WireSource::UnaryOp{op, right} => { let right = self.get_wire_or_constant_as_wire(right)?; - (None, RealWireDataSource::UnaryOp{op: *op, right}) + (None, RealWireDataSource::UnaryOp{op: op, right}) } - WireSource::BinaryOp{op, left, right} => { + &WireSource::BinaryOp{op, left, right} => { let left = self.get_wire_or_constant_as_wire(left)?; let right = self.get_wire_or_constant_as_wire(right)?; - (None, RealWireDataSource::BinaryOp{op: *op, left, right}) + (None, RealWireDataSource::BinaryOp{op: op, left, right}) } - WireSource::ArrayAccess{arr, arr_idx} => { + &WireSource::ArrayAccess{arr, arr_idx} => { let arr = self.get_wire_or_constant_as_wire(arr)?; - match &self.generation_state[arr_idx.0] { + match &self.generation_state[arr_idx] { SubModuleOrWire::SubModule(_) => unreachable!(), SubModuleOrWire::Unnasigned => unreachable!(), SubModuleOrWire::Wire(w) => { (None, RealWireDataSource::ArrayAccess{arr, arr_idx: *w}) } SubModuleOrWire::CompileTimeValue(v) => { - let arr_idx = self.extract_integer_from_value(v, arr_idx.1)?; + let arr_idx_wire = self.module.flattened.instantiations[arr_idx].extract_wire(); + let arr_idx = self.extract_integer_from_value(v, arr_idx_wire.span)?; (None, RealWireDataSource::ConstArrayAccess{arr, arr_idx}) } }