From 0505b50dd083552810f447dce3b94af392ca2ff8 Mon Sep 17 00:00:00 2001 From: Lennart Van Hirtum Date: Wed, 10 Jan 2024 21:16:51 +0100 Subject: [PATCH] First steps towards doing linking while flattening --- src/flattening.rs | 48 +++++------ src/instantiation/mod.rs | 4 +- src/linker.rs | 169 ++++++++++++++++++++++++++++----------- 3 files changed, 142 insertions(+), 79 deletions(-) diff --git a/src/flattening.rs b/src/flattening.rs index 8b6be50..edaae77 100644 --- a/src/flattening.rs +++ b/src/flattening.rs @@ -1,8 +1,8 @@ -use std::{ops::{Deref, Range}, iter::zip}; +use std::{ops::{Deref, Range}, iter::zip, cell::{RefCell, Ref}}; use crate::{ ast::{Span, Module, Expression, SpanExpression, LocalOrGlobal, Operator, AssignableExpression, SpanAssignableExpression, Statement, CodeBlock, IdentifierType, TypeExpression, DeclIDMarker, DeclID, SpanTypeExpression}, - linker::{Linker, Named, Linkable, NamedUUID, FileUUID}, + linker::{Linker, Named, Linkable, NamedUUID, FileUUID, GlobalResolver, ResolvedGlobals}, errors::{ErrorCollector, error_info}, arena_alloc::{UUID, UUIDMarker, FlatAlloc, UUIDRange}, typing::{Type, typecheck_unary_operator, get_binary_operator_types, typecheck, typecheck_is_array_indexer, BOOL_TYPE, INT_TYPE}, value::Value }; @@ -178,37 +178,23 @@ impl Instantiation { } } -struct FlatteningContext<'inst, 'l, 'm> { +struct FlatteningContext<'inst, 'l, 'm, 'resolved> { decl_to_flat_map : FlatAlloc, DeclIDMarker>, instantiations : &'inst mut FlatAlloc, errors : ErrorCollector, is_remote_declaration : Option, - linker : &'l Linker, + linker : GlobalResolver<'l, 'resolved>, module : &'m Module, } -impl<'inst, 'l, 'm> FlatteningContext<'inst, 'l, 'm> { - pub fn map_to_type(&mut self, type_expr : &SpanTypeExpression) -> Type { +impl<'inst, 'l, 'm, 'resolved> FlatteningContext<'inst, 'l, 'm, 'resolved> { + fn map_to_type(&mut self, type_expr : &SpanTypeExpression) -> Type { match &type_expr.0 { TypeExpression::Named(n) => { - if let Some(found_ref) = self.module.link_info.global_references[*n].1 { - let named = &self.linker.links.globals[found_ref]; - match named { - Named::Module(_) => { - self.errors.error_basic(type_expr.1, format!("This should be a type, but it refers to the module '{}'", named.get_full_name())); - Type::Error - } - Named::Constant(_) => { - self.errors.error_basic(type_expr.1, format!("This should be a type, but it refers to the constant '{}'", named.get_full_name())); - Type::Error - } - Named::Type(_typ) => { - Type::Named(found_ref) - } - } + if let Some(typ_id) = &self.linker.try_get_type(self.module.link_info.global_references[*n], &self.errors) { + Type::Named(*typ_id) } else { - // Error report handled by linker Type::Error } } @@ -233,7 +219,7 @@ impl<'inst, 'l, 'm> FlatteningContext<'inst, 'l, 'm> { if ONLY_ALLOW_TYPES { if let TypeExpression::Named(n) = &decl.typ.0 { if let Some(possible_module_ref) = self.module.link_info.global_references[*n].1 { - if let Named::Module(md) = &self.linker.links.globals[possible_module_ref] { + if let Some(md) = &self.linker.is_module(possible_module_ref) { return self.alloc_module_interface(decl.name.clone(), md,possible_module_ref, decl.typ.1) } } @@ -270,7 +256,7 @@ impl<'inst, 'l, 'm> FlatteningContext<'inst, 'l, 'm> { instantiations: self.instantiations, errors: ErrorCollector::new(module.link_info.file), // Temporary ErrorCollector, unused is_remote_declaration: Some(module_uuid), - linker: self.linker, + linker: self.linker.new_sublinker(module.link_info.file), module, }; @@ -304,7 +290,7 @@ impl<'inst, 'l, 'm> FlatteningContext<'inst, 'l, 'm> { } }; let func_instantiation = &self.instantiations[func_instantiation_id].extract_submodule(); - let Named::Module(md) = &self.linker.links.globals[func_instantiation.module_uuid] else {unreachable!("UUID Should be a module!")}; + let md = self.linker.get_module(func_instantiation.module_uuid); let submodule_local_wires = func_instantiation.local_wires.clone(); @@ -528,7 +514,8 @@ pub struct FlattenedModule { pub instantiations : FlatAlloc, pub errors : ErrorCollector, pub outputs_start : usize, - pub interface_ports : Box<[FlatID]> + pub interface_ports : Box<[FlatID]>, + pub resolved_globals : ResolvedGlobals } impl FlattenedModule { @@ -537,7 +524,8 @@ impl FlattenedModule { instantiations : FlatAlloc::new(), errors : ErrorCollector::new(file), interface_ports : Box::new([]), - outputs_start : 0 + outputs_start : 0, + resolved_globals : ResolvedGlobals::new() } } @@ -560,12 +548,13 @@ impl FlattenedModule { */ pub fn initialize(linker : &Linker, module : &Module, starts_with_errors : bool) -> FlattenedModule { let mut instantiations = FlatAlloc::new(); + let resolved_globals : RefCell = RefCell::new(ResolvedGlobals::new()); let mut context = FlatteningContext{ decl_to_flat_map: module.declarations.iter().map(|_| None).collect(), instantiations: &mut instantiations, errors: ErrorCollector::new(module.link_info.file), is_remote_declaration : None, - linker, + linker : GlobalResolver::new(linker, module.link_info.file, &resolved_globals), module, }; context.errors.did_error.set(starts_with_errors); @@ -578,7 +567,8 @@ impl FlattenedModule { errors : context.errors, instantiations : instantiations, interface_ports, - outputs_start : module.outputs_start + outputs_start : module.outputs_start, + resolved_globals : resolved_globals.into_inner() } } diff --git a/src/instantiation/mod.rs b/src/instantiation/mod.rs index c59b18f..4a58b4a 100644 --- a/src/instantiation/mod.rs +++ b/src/instantiation/mod.rs @@ -381,9 +381,9 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> { let typ = self.concretize_type(&wire_decl.typ, wire_decl.typ_span)?; if wire_decl.identifier_type == IdentifierType::Generative { /*Do nothing (in fact re-initializes the wire to 'empty'), just corresponds to wire declaration*/ - if wire_decl.read_only { + /*if wire_decl.read_only { // Don't know why this check is *here* todo!("Modules can't be computed at compile time yet"); - } + }*/ let initial_value = typ.get_initial_val(self.linker); assert!(initial_value.is_of_type(&typ)); SubModuleOrWire::CompileTimeValue(initial_value) diff --git a/src/linker.rs b/src/linker.rs index 0f87634..cb6d2ac 100644 --- a/src/linker.rs +++ b/src/linker.rs @@ -1,4 +1,4 @@ -use std::{collections::{HashMap, HashSet}, ops::{IndexMut, Index}, rc::Rc}; +use std::{collections::{HashMap, HashSet}, ops::{IndexMut, Index}, rc::Rc, cell::RefCell}; use crate::{ast::{Module, LinkInfo, Span, GlobalReference}, arena_alloc::{ArenaAllocator, UUID, UUIDMarker}, parser::{FullParseResult, TokenTreeNode}, tokenizer::Token, errors::{ErrorCollector, error_info}, flattening::FlattenedModule, util::{const_str_position, const_str_position_in_tuples}, instantiation::InstantiatedModule, value::Value}; @@ -424,14 +424,105 @@ impl Linker { self.add_reserved_file(file, parse_result); } - pub fn get_module(&self, uuid : NamedUUID) -> &Module { - let Named::Module(md) = &self.links.globals[uuid] else {unreachable!()}; - md + pub fn recompile_all(&mut self) { + // First create initial flattening for everything, to produce the necessary interfaces + + let module_ids : Vec = self.links.globals.iter().filter_map(|(id,v)| { + if let Named::Module(_) = v { + Some(id) + } else { + None + } + }).collect(); + for id in &module_ids { + let Named::Module(md) = &self.links.globals[*id] else {unreachable!()}; + + println!("Flattening {}", md.link_info.name); + + let mut flattened = FlattenedModule::initialize(&self, md, !md.link_info.is_fully_linked); + println!("Typechecking {}", &md.link_info.name); + flattened.typecheck(self); + flattened.find_unused_variables(); + + let Named::Module(md) = &mut self.links.globals[*id] else {unreachable!()}; + md.flattened = flattened; + md.instantiations.clear_instances(); + } + + // Can't merge these loops, because instantiation can only be done once all modules have been type checked + for (id, named_object) in &self.links.globals { + if let Named::Module(md) = named_object { + println!("[[{}]]:", md.link_info.name); + md.print_flattened_module(); + let inst = self.instantiate(id); + } + } + } + + pub fn instantiate(&self, module_id : NamedUUID) -> Option> { + let Named::Module(md) = &self.links.globals[module_id] else {panic!("{module_id:?} is not a Module!")}; + println!("Instantiating {}", md.link_info.name); + + md.instantiations.instantiate(&md.link_info.name, &md.flattened, self) + } +} + +#[derive(Debug)] +pub struct ResolvedGlobals { + referenced_globals : Vec, + all_resolved : bool +} + +impl ResolvedGlobals { + pub fn new() -> ResolvedGlobals { + ResolvedGlobals{referenced_globals : Vec::new(), all_resolved : true} + } +} + +pub struct GlobalResolver<'linker, 'resolved_list> { + linker : &'linker Linker, + file : &'linker FileData, + + resolved_globals : &'resolved_list RefCell +} + +impl<'linker, 'resolved_list> GlobalResolver<'linker, 'resolved_list> { + pub fn new(linker : &'linker Linker, file_id : FileUUID, resolved_globals : &'resolved_list RefCell) -> GlobalResolver<'linker, 'resolved_list> { + GlobalResolver{linker, file : &linker.files[file_id], resolved_globals} + } + + pub fn new_sublinker(&self, file_id : FileUUID) -> GlobalResolver<'linker, 'resolved_list> { + GlobalResolver{linker : self.linker, file : &self.linker.files[file_id], resolved_globals : self.resolved_globals} + } + + pub fn resolve_global(&self, name : Span) -> Option { + let name = self.file.get_token_text(name.assert_is_single_token()); + + let mut resolved_globals = self.resolved_globals.borrow_mut(); + if let Some(found) = self.linker.links.global_namespace.get(name) { + resolved_globals.referenced_globals.push(*found); + Some(*found) + } else { + resolved_globals.all_resolved = false; + None + } + } + + pub fn get_module(&self, uuid : NamedUUID) -> &'linker Module { + self.is_module(uuid).unwrap() + } + + pub fn is_module(&self, uuid : NamedUUID) -> Option<&'linker Module> { + if let Named::Module(md) = &self.linker.links.globals[uuid] { + Some(md) + } else { + None + } } pub fn try_get_constant(&self, GlobalReference(identifier_span, ref_uuid) : GlobalReference, errors : &ErrorCollector) -> Option { if let Some(uuid) = ref_uuid { - match &self.links.globals[uuid] { + match &self.linker.links.globals[uuid] { Named::Constant(NamedConstant::Builtin(_name, v)) => { Some(v.clone()) }, @@ -453,11 +544,11 @@ impl Linker { } } - pub fn try_get_module(&self, GlobalReference(identifier_span, ref_uuid) : GlobalReference, errors : &ErrorCollector) -> Option<&Module> { + pub fn try_get_type(&self, GlobalReference(identifier_span, ref_uuid) : GlobalReference, errors : &ErrorCollector) -> Option { if let Some(uuid) = ref_uuid { - match &self.links.globals[uuid] { - Named::Module(md) => { - Some(md) + match &self.linker.links.globals[uuid] { + Named::Type(_t) => { + Some(uuid) }, other => { let info = other.get_linking_error_location(); @@ -468,7 +559,7 @@ impl Linker { }; let name = info.name; let ident_type = info.named_type; - errors.error_with_info(identifier_span, format!("{ident_type} {name} is not a Module!"), infos); + errors.error_with_info(identifier_span, format!("{ident_type} {name} is not a Type!"), infos); None } } @@ -477,45 +568,27 @@ impl Linker { } } - pub fn recompile_all(&mut self) { - // First create initial flattening for everything, to produce the necessary interfaces - - let module_ids : Vec = self.links.globals.iter().filter_map(|(id,v)| { - if let Named::Module(_) = v { - Some(id) - } else { - None - } - }).collect(); - for id in &module_ids { - let Named::Module(md) = &self.links.globals[*id] else {unreachable!()}; - - println!("Flattening {}", md.link_info.name); - - let mut flattened = FlattenedModule::initialize(&self, md, !md.link_info.is_fully_linked); - println!("Typechecking {}", &md.link_info.name); - flattened.typecheck(self); - flattened.find_unused_variables(); - - let Named::Module(md) = &mut self.links.globals[*id] else {unreachable!()}; - md.flattened = flattened; - md.instantiations.clear_instances(); - } - - // Can't merge these loops, because instantiation can only be done once all modules have been type checked - for (id, named_object) in &self.links.globals { - if let Named::Module(md) = named_object { - println!("[[{}]]:", md.link_info.name); - md.print_flattened_module(); - let inst = self.instantiate(id); + pub fn try_get_module(&self, GlobalReference(identifier_span, ref_uuid) : GlobalReference, errors : &ErrorCollector) -> Option<&'linker Module> { + if let Some(uuid) = ref_uuid { + match &self.linker.links.globals[uuid] { + Named::Module(md) => { + Some(md) + }, + other => { + let info = other.get_linking_error_location(); + let infos = if let Some((file, span)) = info.location { + vec![error_info(span, file, "Defined here")] + } else { + vec![] + }; + let name = info.name; + let ident_type = info.named_type; + errors.error_with_info(identifier_span, format!("{ident_type} {name} is not a Module!"), infos); + None + } } + } else { + None } } - - pub fn instantiate(&self, module_id : NamedUUID) -> Option> { - let Named::Module(md) = &self.links.globals[module_id] else {panic!("{module_id:?} is not a Module!")}; - println!("Instantiating {}", md.link_info.name); - - md.instantiations.instantiate(&md.link_info.name, &md.flattened, self) - } }