Skip to content

Commit

Permalink
First Block Instruction! if Statement. Also generative now
Browse files Browse the repository at this point in the history
  • Loading branch information
VonTum committed Jan 8, 2024
1 parent 00f2a90 commit 4e9af82
Show file tree
Hide file tree
Showing 7 changed files with 219 additions and 156 deletions.
13 changes: 13 additions & 0 deletions multiply_add.sus
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,11 @@ module generative : int in -> int o, int o2 {
a[0] = 10;
gen int[3] xx = a;

gen bool test = true;

if test {

}

o = a[in];
o2 = a[a[0]];
Expand Down Expand Up @@ -339,6 +344,14 @@ module first_bit_idx_6 : bool[6] bits -> int first, bool all_zeros {
} else {
all_zeros = true;
}

/*first int i in 0..6 where bits[i] {
first = i;
all_zeros = false;
} else {
all_zeros = true;
}*/

}

module first_bit_idx_24 : bool[24] bits -> int first {
Expand Down
33 changes: 21 additions & 12 deletions src/arena_alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,13 @@ impl<IndexMarker : UUIDMarker> UUID<IndexMarker> {
}
}

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct UUIDRange<IndexMarker : UUIDMarker>(pub UUID<IndexMarker>, pub UUID<IndexMarker>);

impl<IndexMarker : UUIDMarker> IntoIterator for &UUIDRange<IndexMarker> {
type Item = UUID<IndexMarker>;

type IntoIter = UUIDRangeIter<IndexMarker>;

fn into_iter(self) -> UUIDRangeIter<IndexMarker> {
UUIDRangeIter(UUID(self.0.0, PhantomData), UUID(self.1.0, PhantomData))
}
}

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct UUIDRangeIter<IndexMarker : UUIDMarker>(UUID<IndexMarker>, UUID<IndexMarker>);

impl<IndexMarker : UUIDMarker> Iterator for UUIDRangeIter<IndexMarker> {
impl<IndexMarker : UUIDMarker> Iterator for UUIDRange<IndexMarker> {
type Item = UUID<IndexMarker>;

fn next(&mut self) -> Option<Self::Item> {
Expand All @@ -59,6 +49,19 @@ impl<IndexMarker : UUIDMarker> Iterator for UUIDRangeIter<IndexMarker> {
}
}

impl<IndexMarker : UUIDMarker> UUIDRange<IndexMarker> {
pub fn skip_to(&mut self, to : UUID<IndexMarker>) {
assert!(to.0 >= self.0.0);
assert!(to.0 <= self.1.0);
self.0 = to;
}
pub fn len(&self) -> usize {
self.1.0 - self.0.0
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}



Expand Down Expand Up @@ -275,6 +278,9 @@ impl<T, IndexMarker : UUIDMarker> ListAllocator<T, IndexMarker> {
pub fn get_next_alloc_id(&self) -> UUID<IndexMarker> {
UUID(self.data.len(), PhantomData)
}
pub fn id_range(&self) -> UUIDRange<IndexMarker> {
UUIDRange(UUID(0, PhantomData), UUID(self.data.len(), PhantomData))
}
pub fn iter<'a>(&'a self) -> ListAllocIterator<'a, T, IndexMarker> {
self.into_iter()
}
Expand Down Expand Up @@ -387,6 +393,9 @@ impl<T, IndexMarker : UUIDMarker> FlatAlloc<T, IndexMarker> {
pub fn len(&self) -> usize {
self.data.len()
}
pub fn id_range(&self) -> UUIDRange<IndexMarker> {
UUIDRange(UUID(0, PhantomData), UUID(self.data.len(), PhantomData))
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
Expand Down
2 changes: 1 addition & 1 deletion src/dev_aid/syntax_highlighting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ fn walk_name_color(all_objects : &[NamedUUID], links : &Links, result : &mut [ID
if decl.identifier_type == IdentifierType::Virtual {continue;} // Virtual wires don't appear in the program text
result[conn.to.span.0].typ = IDETokenType::Identifier(IDEIdentifierType::Value(decl.identifier_type));
}
Instantiation::SubModule(_) => {}
Instantiation::SubModule(_) | Instantiation::IfStatement(_) => {}
}
}
}
Expand Down
139 changes: 73 additions & 66 deletions src/flattening.rs

Large diffs are not rendered by default.

119 changes: 77 additions & 42 deletions src/instantiation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::{rc::Rc, ops::Deref, cell::RefCell};

use num::BigInt;

use crate::{arena_alloc::{UUID, UUIDMarker, FlatAlloc}, ast::{Operator, IdentifierType, Span}, typing::{ConcreteType, Type}, flattening::{FlatID, Instantiation, FlatIDMarker, ConnectionWritePathElement, WireSource, WireInstance, Connection, ConnectionWritePathElementComputed, FlattenedModule}, errors::ErrorCollector, linker::{Linker, get_builtin_uuid}, value::{Value, compute_unary_op, compute_binary_op}};
use crate::{arena_alloc::{UUID, UUIDMarker, FlatAlloc, UUIDRange}, ast::{Operator, IdentifierType, Span}, typing::{ConcreteType, Type, BOOL_CONCRETE_TYPE, INT_CONCRETE_TYPE}, flattening::{FlatID, Instantiation, FlatIDMarker, ConnectionWritePathElement, WireSource, WireInstance, Connection, ConnectionWritePathElementComputed, FlattenedModule, FlatIDRange}, errors::ErrorCollector, linker::Linker, value::{Value, compute_unary_op, compute_binary_op}, tokenizer::kw};

pub mod latency;

Expand Down Expand Up @@ -125,11 +125,6 @@ impl SubModuleOrWire {
*result
}
#[track_caller]
fn extract_submodule(&self) -> SubModuleID {
let Self::SubModule(result) = self else {panic!("Failed SubModule extraction! Is {self:?} instead")};
*result
}
#[track_caller]
fn extract_generation_value(&self) -> &Value {
let Self::CompileTimeValue(result) = self else {panic!("Failed GenerationValue extraction! Is {self:?} instead")};
result
Expand Down Expand Up @@ -159,8 +154,24 @@ struct InstantiationContext<'fl, 'l> {
}

impl<'fl, 'l> InstantiationContext<'fl, 'l> {
fn get_generation_value(&self, v : FlatID) -> Option<&Value> {
if let SubModuleOrWire::CompileTimeValue(vv) = &self.generation_state[v] {
if let Value::Unset = vv {
self.errors.error_basic(self.flattened.instantiations[v].extract_wire().span, "This variable is set but it's Value::Unset!");
None
} else if let Value::Error = vv {
self.errors.error_basic(self.flattened.instantiations[v].extract_wire().span, "This variable is set but it's Value::Error!");
None
} else {
Some(vv)
}
} else {
self.errors.error_basic(self.flattened.instantiations[v].extract_wire().span, "This variable is not set at this point!");
None
}
}
fn extract_integer_from_value<'v, IntT : TryFrom<&'v BigInt>>(&self, val : &'v Value, span : Span) -> Option<IntT> {
let Value::Integer(val) = val else {self.errors.error_basic(span, format!("Value is not an int, it is {val:?} instead")); return None};
let val = val.extract_integer(); // Typecheck should cover this
match IntT::try_from(val) {
Ok(val) => Some(val),
Err(_) => {
Expand All @@ -169,6 +180,10 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> {
}
}
}
fn extract_bool_from_value(&self, val : &Value, span : Span) -> Option<bool> {
let Value::Bool(val) = val else {self.errors.error_basic(span, format!("Value is not a bool, it is {val:?} instead")); return None};
Some(*val)
}
fn concretize_type(&self, typ : &Type, span : Span) -> Option<ConcreteType> {
match typ {
Type::Error | Type::Unknown => unreachable!("Bad types should be caught in flattening: {}", typ.to_string(self.linker)),
Expand All @@ -185,7 +200,7 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> {
}
}
}
fn process_connection_to_wire(&mut self, to_path : &[ConnectionWritePathElement], from : ConnectFrom, wire_id : WireID) {
fn process_connection_to_wire(&mut self, to_path : &[ConnectionWritePathElement], from : ConnectFrom, wire_id : WireID) -> Option<()> {
let mut new_path : Vec<ConnectToPathElem> = Vec::new();

let mut write_to_typ = &self.wires[wire_id].typ;
Expand All @@ -197,12 +212,12 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> {
SubModuleOrWire::SubModule(_) => unreachable!(),
SubModuleOrWire::Unnasigned => unreachable!(),
SubModuleOrWire::Wire(idx_wire) => {
assert!(self.wires[*idx_wire].typ == ConcreteType::Named(get_builtin_uuid("int")));
assert!(self.wires[*idx_wire].typ == INT_CONCRETE_TYPE);

new_path.push(ConnectToPathElem::MuxArrayWrite{idx_wire : *idx_wire});
}
SubModuleOrWire::CompileTimeValue(v) => {
let Some(idx) = self.extract_integer_from_value(v, *idx_span) else {return};
let idx = self.extract_integer_from_value(v, *idx_span)?;
new_path.push(ConnectToPathElem::ConstArrayWrite{idx});
}
}
Expand All @@ -221,59 +236,49 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> {
let RealWireDataSource::Multiplexer{is_state : _, sources} = &mut self.wires[wire_id].source else {unreachable!("Should only be a writeable wire here")};

sources.push(MultiplexerSource{from, path : new_path});

Some(())
}
fn convert_connection_path_to_known_values(&self, conn_path : &[ConnectionWritePathElement]) -> Option<Vec<ConnectionWritePathElementComputed>> {
let mut result = Vec::new();
result.reserve(conn_path.len());
for p in conn_path {
match p {
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::<usize>(idx_val, *idx_span) else {return None};
let idx_val = self.get_generation_value(*idx)?;
let idx_val = self.extract_integer_from_value::<usize>(idx_val, *idx_span)?;
result.push(ConnectionWritePathElementComputed::ArrayIdx(idx_val))
}
}
}
Some(result)
}
fn process_connection(&mut self, conn : &Connection, original_wire : FlatID) {
fn process_connection(&mut self, conn : &Connection, original_wire : FlatID, condition : Option<WireID>) -> Option<()> {
match &self.generation_state[conn.to.root] {
SubModuleOrWire::SubModule(_) => unreachable!(),
SubModuleOrWire::Unnasigned => unreachable!(),
SubModuleOrWire::Wire(w) => { // Runtime wire
let deref_w = *w;

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 from = self.get_wire_or_constant_as_wire(conn.from)?;
let conn_from = ConnectFrom {
num_regs: conn.num_regs,
from,
condition,
original_wire
};

self.process_connection_to_wire(&conn.to.path, conn_from, deref_w);

return;
self.process_connection_to_wire(&conn.to.path, conn_from, deref_w)?;
}
SubModuleOrWire::CompileTimeValue(_original_value) => { // Compiletime wire
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};
let cvt_path = self.convert_connection_path_to_known_values(&conn.to.path)?;
// Hack to get around the borrow rules here
let SubModuleOrWire::CompileTimeValue(v_writable) = &mut self.generation_state[conn.to.root] else {unreachable!()};
write_gen_variable(v_writable, &cvt_path, found_v);
}
};

}
fn get_generation_value(&self, v : FlatID) -> Option<&Value> {
if let SubModuleOrWire::CompileTimeValue(vv) = &self.generation_state[v] {
Some(vv)
} else {
self.errors.error_basic(self.flattened.instantiations[v].extract_wire().span, "This variable is not set at this point!");
None
}
Some(())
}
fn compute_compile_time(&self, wire_inst : &WireSource) -> Option<Value> {
Some(match wire_inst {
Expand Down Expand Up @@ -358,9 +363,17 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> {
let name = self.get_unique_name();
Some(self.wires.alloc(RealWire{ name, typ, original_wire, source}))
}
fn instantiate_flattened_module(&mut self) {
for (original_wire, inst) in &self.flattened.instantiations {
let instance_to_add : SubModuleOrWire = match inst {
fn extend_condition(&mut self, condition : Option<WireID>, additional_condition : WireID, original_wire : FlatID) -> WireID {
if let Some(condition) = condition {
self.wires.alloc(RealWire{typ : BOOL_CONCRETE_TYPE, name : self.get_unique_name(), original_wire, source : RealWireDataSource::BinaryOp{op: Operator{op_typ : kw("&")}, left : condition, right : additional_condition}})
} else {
additional_condition
}
}
fn instantiate_flattened_module(&mut self, flat_range : FlatIDRange, condition : Option<WireID>) -> Option<()> {
let mut instruction_range = flat_range.into_iter();
while let Some(original_wire) = instruction_range.next() {
let instance_to_add : SubModuleOrWire = match &self.flattened.instantiations[original_wire] {
Instantiation::SubModule(submodule) => {
let Some(instance) = self.linker.instantiate(submodule.module_uuid) else {continue}; // Avoid error from submodule
let interface_real_wires = submodule.local_wires.iter().map(|port| {
Expand All @@ -369,9 +382,7 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> {
SubModuleOrWire::SubModule(self.submodules.alloc(SubModule { original_flat: original_wire, instance, wires : interface_real_wires, name : submodule.name.clone()}))
}
Instantiation::WireDeclaration(wire_decl) => {
let Some(typ) = self.concretize_type(&wire_decl.typ, wire_decl.typ_span) else {
return; // Exit early, do not produce invalid wires in InstantiatedModule
};
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 {
Expand All @@ -392,25 +403,49 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> {
}
}
Instantiation::Wire(w) => {
let Some(typ) = self.concretize_type(&w.typ, w.span) else {
return; // Exit early, do not produce invalid wires in InstantiatedModule
};
let typ = self.concretize_type(&w.typ, w.span)?;
if w.is_compiletime {
let Some(value_computed) = self.compute_compile_time(&w.source) else {return};
let value_computed = self.compute_compile_time(&w.source)?;
assert!(value_computed.is_of_type(&typ));
SubModuleOrWire::CompileTimeValue(value_computed)
} else {
let Some(wire_found) = self.wire_to_real_wire(w, typ, original_wire) else {return};
let wire_found = self.wire_to_real_wire(w, typ, original_wire)?;
SubModuleOrWire::Wire(wire_found)
}
}
Instantiation::Connection(conn) => {
self.process_connection(conn, original_wire);
self.process_connection(conn, original_wire, condition);
continue;
}
Instantiation::IfStatement(stm) => {
let then_range = UUIDRange(stm.then_start, stm.then_end_else_start);
let else_range = UUIDRange(stm.then_end_else_start, stm.else_end);
if stm.is_compiletime {
let condition_val = self.get_generation_value(stm.condition)?;
let run_range = if condition_val.extract_bool() {
then_range
} else {
else_range
};
self.instantiate_flattened_module(run_range, condition);
} else {
let condition_wire = self.generation_state[stm.condition].extract_wire();
let then_cond = self.extend_condition(condition, condition_wire, original_wire);
self.instantiate_flattened_module(then_range, Some(then_cond));

if !else_range.is_empty() {
let else_condition_bool = self.wires.alloc(RealWire{typ : BOOL_CONCRETE_TYPE, name : self.get_unique_name(), original_wire, source : RealWireDataSource::UnaryOp{op : Operator{op_typ : kw("!")}, right : condition_wire}});
let else_cond = self.extend_condition(condition, else_condition_bool, original_wire);
self.instantiate_flattened_module(else_range, Some(else_cond));
}
}
instruction_range.skip_to(stm.else_end);
continue;
}
};
self.generation_state[original_wire] = instance_to_add;
}
Some(())
}

// Returns a proper interface if all ports involved did not produce an error. If a port did produce an error then returns None.
Expand Down Expand Up @@ -467,7 +502,7 @@ impl InstantiationList {
errors : ErrorCollector::new(flattened.errors.file)
};

context.instantiate_flattened_module();
context.instantiate_flattened_module(flattened.instantiations.id_range(), None);
let interface = context.make_interface();

cache_borrow.push(Rc::new(InstantiatedModule{
Expand Down
Loading

0 comments on commit 4e9af82

Please sign in to comment.