Skip to content

Commit

Permalink
Move instantiate submodule to concrete_typecheck
Browse files Browse the repository at this point in the history
  • Loading branch information
VonTum committed Nov 17, 2024
1 parent a3716d0 commit 41c7c97
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 111 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ To get started with learning SUS, have a look at [this talk](https://www.youtube
For an example project to tinker with, see [VonTum/BitSerialMatrixMultiply](https://github.com/VonTum/BitSerialMatrixMultiply).

#### Changelog since Talk
- Nothing yet
- Template syntax has changed to `#(NameA: 3, TypeB: type int[3], ValueC: true)`
- Standard Library is delivered with SUS Compiler
- Hindley-Milner for Concrete Typing

## Core philosophy

Expand Down
2 changes: 1 addition & 1 deletion src/codegen_fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ impl<'g, 'out, Stream: std::fmt::Write> CodeGenerationContext<'g, 'out, Stream>
for (_id, sm) in &self.instance.submodules {
let sm_inst: &InstantiatedModule = sm
.instance
.as_ref()
.get()
.expect("Invalid submodules are impossible to remain by the time codegen happens");
let sm_instance_name = mangle(&sm_inst.name);
let sm_name = &sm.name;
Expand Down
100 changes: 100 additions & 0 deletions src/instantiation/concrete_typecheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,106 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> {
}
}

pub fn instantiate_submodule(&self, sm: &SubModule) -> bool {
let submod_instr = self.md.link_info.instructions[sm.original_instruction].unwrap_submodule();
let sub_module = &self.linker.modules[sm.module_uuid];

if !check_all_template_args_valid(
&self.errors,
submod_instr.module_ref.get_total_span(),
&sub_module.link_info,
&sm.template_args,
) {
return false;
};

if let Some(instance) = sub_module.instantiations.instantiate(
sub_module,
self.linker,
sm.template_args.clone(),
) {
for (port_id, concrete_port) in &instance.interface_ports {
let connecting_wire = &sm.port_map[port_id];

match (concrete_port, connecting_wire) {
(None, None) => {} // Invalid port not connected, good!
(None, Some(connecting_wire)) => {
// Port is not enabled, but attempted to be used
// A question may be "What if no port was in the source code? There would be no error reported"
// But this is okay, because non-visible ports are only possible for function calls
// We have a second routine that reports invalid interfaces.
let source_code_port = &sub_module.ports[port_id];
for span in &connecting_wire.name_refs {
self.errors.error(*span, format!("Port '{}' is used, but the instantiated module has this port disabled", source_code_port.name))
.info_obj_different_file(source_code_port, sub_module.link_info.file)
.info_obj_same_file(submod_instr);
}
}
(Some(_concrete_port), None) => {
// Port is enabled, but not used
let source_code_port = &sub_module.ports[port_id];
self.errors
.warn(
submod_instr.module_ref.get_total_span(),
format!("Unused port '{}'", source_code_port.name),
)
.info_obj_different_file(
source_code_port,
sub_module.link_info.file,
)
.info_obj_same_file(submod_instr);
}
(Some(concrete_port), Some(connecting_wire)) => {
let wire = &self.wires[connecting_wire.maps_to_wire];
self.type_substitutor.unify_must_succeed(&wire.typ, &concrete_port.typ)
}
}
}
for (interface_id, interface_references) in &sm.interface_call_sites {
if !interface_references.is_empty() {
let sm_interface = &sub_module.interfaces[interface_id];
let interface_name = &sm_interface.name;
if let Some(representative_port) = sm_interface
.func_call_inputs
.first()
.or(sm_interface.func_call_outputs.first())
{
if instance.interface_ports[representative_port].is_none() {
for span in interface_references {
self.errors.error(*span, format!("The interface '{interface_name}' is disabled in this submodule instance"))
.info_obj_same_file(submod_instr)
.info((sm_interface.name_span, sub_module.link_info.file), format!("Interface '{interface_name}' declared here"));
}
}
} else {
for span in interface_references {
self.errors.todo(*span, format!("Using empty interface '{interface_name}' (This is a TODO with Actions etc)"))
.info_obj_same_file(submod_instr)
.info((sm_interface.name_span, sub_module.link_info.file), format!("Interface '{interface_name}' declared here"));
}
}
if sm_interface
.all_ports()
.iter()
.any(|port_id| instance.interface_ports[port_id].is_none())
{
// We say an interface is invalid if it has an invalid port.
todo!("Invalid Interfaces");
}
}
}

sm.instance.set(instance).expect("Can only set the instance of a submodule once");
true
} else {
self.errors.error(
submod_instr.module_ref.get_total_span(),
"Error instantiating submodule",
);
false
}
}

fn finalize(&mut self) {
for (_id, w) in &mut self.wires {
if let Err(()) = w.typ.fully_substitute(&self.type_substitutor) {
Expand Down
2 changes: 1 addition & 1 deletion src/instantiation/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,7 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> {
}
SubModuleOrWire::SubModule(self.submodules.alloc(SubModule {
original_instruction,
instance: None,
instance: OnceCell::new(),
port_map,
interface_call_sites,
name : self.unique_name_producer.get_unique_name(name_origin),
Expand Down
2 changes: 1 addition & 1 deletion src/instantiation/latency_count.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> {

for (_sm_id, sm) in &self.submodules {
// Instances may not be valid (or may not exist yet due to inference)
let Some(instance) = &sm.instance else {
let Some(instance) = &sm.instance.get() else {
continue;
};

Expand Down
109 changes: 3 additions & 106 deletions src/instantiation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use unique_names::UniqueNames;
use crate::prelude::*;
use crate::typing::type_inference::{ConcreteTypeVariableIDMarker, TypeSubstitutor};

use std::cell::OnceCell;
use std::{cell::RefCell, collections::HashMap, rc::Rc};

use crate::flattening::{BinaryOperator, Module, UnaryOperator};
Expand Down Expand Up @@ -107,7 +108,7 @@ pub struct UsedPort {
#[derive(Debug)]
pub struct SubModule {
pub original_instruction: FlatID,
pub instance: Option<Rc<InstantiatedModule>>,
pub instance: OnceCell<Rc<InstantiatedModule>>,
pub port_map: FlatAlloc<Option<UsedPort>, PortIDMarker>,
pub interface_call_sites: FlatAlloc<Vec<Span>, InterfaceIDMarker>,
pub name: String,
Expand Down Expand Up @@ -303,110 +304,6 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> {
errors: self.errors.into_storage(),
}
}

fn instantiate_submodules(&mut self) -> bool {
let mut success = true;
for (_sm_id, sm) in &mut self.submodules {
let submod_instr = self.md.link_info.instructions[sm.original_instruction].unwrap_submodule();
let sub_module = &self.linker.modules[sm.module_uuid];

if !check_all_template_args_valid(
&self.errors,
submod_instr.module_ref.get_total_span(),
&sub_module.link_info,
&sm.template_args,
) {
success = false;
continue;
};

if let Some(instance) = sub_module.instantiations.instantiate(
sub_module,
self.linker,
sm.template_args.clone(),
) {
for (port_id, concrete_port) in &instance.interface_ports {
let connecting_wire = &sm.port_map[port_id];

match (concrete_port, connecting_wire) {
(None, None) => {} // Invalid port not connected, good!
(None, Some(connecting_wire)) => {
// Port is not enabled, but attempted to be used
// A question may be "What if no port was in the source code? There would be no error reported"
// But this is okay, because non-visible ports are only possible for function calls
// We have a second routine that reports invalid interfaces.
let source_code_port = &sub_module.ports[port_id];
for span in &connecting_wire.name_refs {
self.errors.error(*span, format!("Port '{}' is used, but the instantiated module has this port disabled", source_code_port.name))
.info_obj_different_file(source_code_port, sub_module.link_info.file)
.info_obj_same_file(submod_instr);
}
}
(Some(_concrete_port), None) => {
// Port is enabled, but not used
let source_code_port = &sub_module.ports[port_id];
self.errors
.warn(
submod_instr.module_ref.get_total_span(),
format!("Unused port '{}'", source_code_port.name),
)
.info_obj_different_file(
source_code_port,
sub_module.link_info.file,
)
.info_obj_same_file(submod_instr);
}
(Some(concrete_port), Some(connecting_wire)) => {
let wire = &mut self.wires[connecting_wire.maps_to_wire];
wire.typ = concrete_port.typ.clone()
}
}
}
for (interface_id, interface_references) in &sm.interface_call_sites {
if !interface_references.is_empty() {
let sm_interface = &sub_module.interfaces[interface_id];
let interface_name = &sm_interface.name;
if let Some(representative_port) = sm_interface
.func_call_inputs
.first()
.or(sm_interface.func_call_outputs.first())
{
if instance.interface_ports[representative_port].is_none() {
for span in interface_references {
self.errors.error(*span, format!("The interface '{interface_name}' is disabled in this submodule instance"))
.info_obj_same_file(submod_instr)
.info((sm_interface.name_span, sub_module.link_info.file), format!("Interface '{interface_name}' declared here"));
}
}
} else {
for span in interface_references {
self.errors.todo(*span, format!("Using empty interface '{interface_name}' (This is a TODO with Actions etc)"))
.info_obj_same_file(submod_instr)
.info((sm_interface.name_span, sub_module.link_info.file), format!("Interface '{interface_name}' declared here"));
}
}
if sm_interface
.all_ports()
.iter()
.any(|port_id| instance.interface_ports[port_id].is_none())
{
// We say an interface is invalid if it has an invalid port.
todo!("Invalid Interfaces");
}
}
}

sm.instance = Some(instance);
} else {
self.errors.error(
submod_instr.module_ref.get_total_span(),
"Error instantiating submodule",
);
success = false;
};
}
success
}
}

fn perform_instantiation(
Expand Down Expand Up @@ -461,7 +358,7 @@ fn perform_instantiation(
}

println!("Instantiating submodules for {}", md.link_info.name);
if !context.instantiate_submodules() {
if !context.submodules.iter().all(|(_id, sm)| context.instantiate_submodule(sm)) {
return context.extract();
}

Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ fn codegen_with_dependencies(linker: &Linker, md: &Module, file_name: &str) {
let (cur_instance, cur_md) = to_process_queue[cur_idx];

for (_, sub_mod) in &cur_instance.submodules {
let new_inst = sub_mod.instance.as_ref().unwrap().as_ref();
let new_inst = sub_mod.instance.get().unwrap().as_ref();

// Skip duplicates
// Yeah yeah I know O(n²) but this list shouldn't grow too big. Fix if needed
Expand Down

0 comments on commit 41c7c97

Please sign in to comment.