diff --git a/Cargo.lock b/Cargo.lock index 294723753..bcdcb33ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -105,7 +105,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "circom" -version = "2.1.9" +version = "2.2.0" dependencies = [ "ansi_term", "clap", @@ -122,7 +122,7 @@ dependencies = [ [[package]] name = "circom_algebra" -version = "2.1.4" +version = "2.2.0" dependencies = [ "constant_tracking", "num-bigint-dig", @@ -146,7 +146,7 @@ dependencies = [ [[package]] name = "code_producers" -version = "2.1.9" +version = "2.2.0" dependencies = [ "handlebars", "lz_fnv", @@ -175,7 +175,7 @@ dependencies = [ [[package]] name = "compiler" -version = "2.1.9" +version = "2.2.0" dependencies = [ "code_producers", "constant_tracking", @@ -190,7 +190,7 @@ version = "2.0.0" [[package]] name = "constraint_generation" -version = "2.1.9" +version = "2.2.0" dependencies = [ "ansi_term", "circom_algebra", @@ -250,7 +250,7 @@ dependencies = [ [[package]] name = "dag" -version = "2.1.8" +version = "2.2.0" dependencies = [ "circom_algebra", "constraint_list", @@ -655,7 +655,7 @@ dependencies = [ [[package]] name = "parser" -version = "2.1.9" +version = "2.2.0" dependencies = [ "lalrpop", "lalrpop-util", @@ -755,7 +755,7 @@ dependencies = [ [[package]] name = "program_structure" -version = "2.1.9" +version = "2.2.0" dependencies = [ "codespan", "codespan-reporting", @@ -1062,7 +1062,7 @@ dependencies = [ [[package]] name = "type_analysis" -version = "2.1.9" +version = "2.2.0" dependencies = [ "num-bigint-dig", "num-traits", diff --git a/RELEASES.md b/RELEASES.md index 182e2b6ed..3f7e198d1 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,4 +1,24 @@ # Release notes +## October 07, 2024 circom 2.2.0 +#### New features +- Buses: more information [here](https://github.com/iden3/circom/blob/master/mkdocs/docs/circom-language/buses.md). + +#### Changes +- input/output keywords are the first token in declarations (though having it after "signal" is still accepted). +- The default option for constraint simplification is --O1 (instead of --O2 which was the default until now). More information in [here](https://github.com/iden3/circom/blob/master/mkdocs/docs/circom-language/circom-insight/simplification.md). + +#### Extensions +- Allowing array assignments of different sizes. +- Improving error reports when parsing. +- Improving documentation. + +#### Fixed bugs +- Main with no inputs is now executed once. +- Fixing complement function to depend on the prime number used. +- Applying modulo prime number to any constant in the circuit. +- Fixing minor panic: the number of signals passed to the anonymous component must be equal to the actual number of inputs. + + ## April 23, 2024 circom 2.1.9 #### Extensions diff --git a/circom/Cargo.toml b/circom/Cargo.toml index a74e55797..a157239b3 100644 --- a/circom/Cargo.toml +++ b/circom/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "circom" -version = "2.1.9" +version = "2.2.0" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/circom/src/input_user.rs b/circom/src/input_user.rs index b77c7c062..ccb4e3ab9 100644 --- a/circom/src/input_user.rs +++ b/circom/src/input_user.rs @@ -269,9 +269,8 @@ mod input_processing { else {Ok(SimplificationStyle::O2(no_rounds))}} else { Result::Err(eprintln!("{}", Colour::Red.paint("invalid number of rounds"))) } }, - (false, false, false, true) => Ok(SimplificationStyle::O2(usize::MAX)), - (false, false, false, false) => Ok(SimplificationStyle::O2(usize::MAX)), + (false, false, false, false) => Ok(SimplificationStyle::O1), } } @@ -413,7 +412,7 @@ mod input_processing { .long("O1") .hidden(false) .takes_value(false) - .help("Only applies signal to signal and signal to constant simplification") + .help("Only applies signal to signal and signal to constant simplification. This is the default option") .display_order(460) ) .arg( diff --git a/circom/src/parser_user.rs b/circom/src/parser_user.rs index 5c72af180..79a1d1c82 100644 --- a/circom/src/parser_user.rs +++ b/circom/src/parser_user.rs @@ -1,4 +1,5 @@ use super::input_user::Input; +use program_structure::constants::UsefulConstants; use program_structure::error_definition::Report; use program_structure::program_archive::ProgramArchive; use crate::VERSION; @@ -6,7 +7,9 @@ use crate::VERSION; pub fn parse_project(input_info: &Input) -> Result { let initial_file = input_info.input_file().to_string(); - let result_program_archive = parser::run_parser(initial_file, VERSION, input_info.get_link_libraries().to_vec(), input_info.save_ast, input_info.ast_path.clone()); + //We get the prime number from the input + let prime = UsefulConstants::new(&input_info.prime()).get_p().clone(); + let result_program_archive = parser::run_parser(initial_file, VERSION, input_info.get_link_libraries().to_vec(), &prime, input_info.save_ast, input_info.ast_path.clone()); match result_program_archive { Result::Err((file_library, report_collection)) => { Report::print_reports(&report_collection, &file_library); diff --git a/circom_algebra/Cargo.toml b/circom_algebra/Cargo.toml index 30e713dbf..2ac52577b 100644 --- a/circom_algebra/Cargo.toml +++ b/circom_algebra/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "circom_algebra" -version = "2.1.4" +version = "2.2.0" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/circom_algebra/src/algebra.rs b/circom_algebra/src/algebra.rs index bb3a47b46..235b7e951 100644 --- a/circom_algebra/src/algebra.rs +++ b/circom_algebra/src/algebra.rs @@ -554,13 +554,13 @@ impl ArithmeticExpression { } // Bit operations - pub fn complement_254( + pub fn complement( elem: &ArithmeticExpression, field: &BigInt, ) -> ArithmeticExpression { use ArithmeticExpression::*; if let Number { value } = elem { - Number { value: modular_arithmetic::complement_254(value, field) } + Number { value: modular_arithmetic::complement(value, field) } } else { NonQuadratic } diff --git a/circom_algebra/src/modular_arithmetic.rs b/circom_algebra/src/modular_arithmetic.rs index 4eb9cc728..60c386c05 100644 --- a/circom_algebra/src/modular_arithmetic.rs +++ b/circom_algebra/src/modular_arithmetic.rs @@ -91,20 +91,20 @@ pub fn multi_inv(values: &Vec, field: &BigInt) -> Vec{ } //Bit operations - -// 254 bit complement -pub fn complement_254(elem: &BigInt, field: &BigInt) -> BigInt { +pub fn complement(elem: &BigInt, field: &BigInt) -> BigInt { let (sign, mut bit_repr) = bit_representation(elem); - while bit_repr.len() > 254 { + let new_sign = if elem == &BigInt::from(0) { Sign::Plus } else { sign}; + let nbits = field.bits(); + while bit_repr.len() > nbits { bit_repr.pop(); } - for _i in bit_repr.len()..254 { + for _i in bit_repr.len()..nbits { bit_repr.push(0); } for bit in &mut bit_repr { *bit = if *bit == 0 { 1 } else { 0 }; } - let cp = BigInt::from_radix_le(sign, &bit_repr, 2).unwrap(); + let cp = BigInt::from_radix_le(new_sign, &bit_repr, 2).unwrap(); modulus(&cp, field) } @@ -252,8 +252,8 @@ mod tests { .expect("generating the big int was not possible"); let big_num = BigInt::parse_bytes("1234".as_bytes(), 10) .expect("generating the big int was not possible"); - let big_num_complement = complement_254(&big_num, &field); - let big_num_complement_complement = complement_254(&big_num_complement, &field); + let big_num_complement = complement(&big_num, &field); + let big_num_complement_complement = complement(&big_num_complement, &field); let big_num_modulus = modulus(&big_num, &field); assert_eq!(big_num_complement_complement, big_num_modulus); } diff --git a/code_producers/Cargo.toml b/code_producers/Cargo.toml index dc024320d..7be46e41a 100644 --- a/code_producers/Cargo.toml +++ b/code_producers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "code_producers" -version = "2.1.9" +version = "2.2.0" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/code_producers/src/c_elements/c_code_generator.rs b/code_producers/src/c_elements/c_code_generator.rs index ed7fa9933..4460b04e1 100644 --- a/code_producers/src/c_elements/c_code_generator.rs +++ b/code_producers/src/c_elements/c_code_generator.rs @@ -18,8 +18,9 @@ const CIRCOM_HASH_ENTRY_FIELDS: [&str; 3] = ["hash", "signalid", "signalsize"]; const S_CIRCOM_COMPONENT: &str = "Circom_Component"; const CIRCOM_COMPONENT_FIELDS: [&str; 4] = ["templateID", "signalStart", "inputCounter", "subcomponents"]; -const S_IO_DEF: &str = "IODef"; -const IO_DEF_FIELDS: [&str; 2] = ["offset", "lengths"]; +const S_IOFIELD_DEF: &str = "IOFieldDef"; +const IOFIELD_DEF_FIELDS: [&str; 4] = ["offset", "size", "lengths", "busid"]; + // Global variables //pub const SIZE_INPUT_HASHMAP: usize = 256; @@ -143,6 +144,11 @@ pub fn template_ins_2_io_info() -> CInstruction { format!("{}", TEMP_INS_2_IO_INFO) } +pub const BUS_INS_2_FIELD_INFO: &str = "busInsId2FieldInfo"; +pub fn bus_ins_2_field_info() -> CInstruction { + format!("{}", BUS_INS_2_FIELD_INFO) +} + pub fn template_id_in_component(idx: CInstruction) -> CInstruction { format!("{}->componentMemory[{}].templateId", CIRCOM_CALC_WIT, idx) } @@ -264,7 +270,7 @@ pub fn my_input_counter() -> CInstruction { pub const TEMPLATE_INS_ID_2_IO_SIGNAL_INFO: &str = "templateInsId2IOSignalInfo"; pub fn declare_template_ins_id_2_io_signal_info() -> CInstruction { format!( - "std::map {} = {}->{}", + "std::map {} = {}->{}", TEMPLATE_INS_ID_2_IO_SIGNAL_INFO, CIRCOM_CALC_WIT, TEMPLATE_INS_ID_2_IO_SIGNAL_INFO ) } @@ -371,6 +377,16 @@ pub fn set_list(elems: Vec) -> String { set_string } +pub fn set_list_tuple(elems: Vec<(usize, usize)>) -> String { + let mut set_string = "{".to_string(); + for (elem_a, elem_b) in elems { + set_string = format!("{}{{{},{}}},", set_string, elem_a, elem_b); + } + set_string.pop(); + set_string .push('}'); + set_string +} + pub fn set_list_bool(elems: Vec) -> String { let mut set_string = "{".to_string(); for elem in elems { @@ -469,16 +485,16 @@ pub fn collect_function_headers(functions: Vec) -> Vec { //--------------- generate all kinds of Data for the .dat file --------------- -pub fn generate_hash_map(signal_name_list: &Vec<(String, usize, usize)>, size: usize) -> Vec<(u64, u64, u64)> { +pub fn generate_hash_map(signal_name_list: &Vec, size: usize) -> Vec<(u64, u64, u64)> { assert!(signal_name_list.len() <= size); let mut hash_map = vec![(0, 0, 0); size]; for i in 0..signal_name_list.len() { - let h = hasher(&signal_name_list[i].0); + let h = hasher(&signal_name_list[i].name); let mut p = h as usize % size; while hash_map[p].1 != 0 { p = (p + 1) % size; } - hash_map[p] = (h, signal_name_list[i].1 as u64, signal_name_list[i].2 as u64); + hash_map[p] = (h, signal_name_list[i].start as u64, signal_name_list[i].size as u64); } hash_map } @@ -617,11 +633,79 @@ pub fn generate_dat_io_signals_info( v.reverse(); io_signals_info.append(&mut v); } + let s32 = s.size as u32; + let mut v: Vec = s32.to_be_bytes().to_vec(); + v.reverse(); + io_signals_info.append(&mut v); + let b32 = if let Some(value) = s.bus_id { + value as u32 + } else { + 0 as u32 + }; + let mut v = b32.to_be_bytes().to_vec(); + v.reverse(); + io_signals_info.append(&mut v); } } io_signals_info } +pub fn generate_dat_bus_field_info( + _producer: &CProducer, + field_map: &FieldMap, +) -> Vec { + // println!("size: {}",field_map.len()); + let mut bus_field_info = vec![]; + //let t32 = field_map.len() as u32; + //let mut v: Vec = t32.to_be_bytes().to_vec(); + //v.reverse(); + //bus_field_info.append(&mut v); + for c in 0..field_map.len() { + //println!("field_def_len: {}",field_map[c].len()); + let l32 = field_map[c].len() as u32; + let mut v: Vec = l32.to_be_bytes().to_vec(); + v.reverse(); + bus_field_info.append(&mut v); + for s in &field_map[c] { + //println!("offset: {}",s.offset); + let l32 = s.offset as u32; + let mut v: Vec = l32.to_be_bytes().to_vec(); + v.reverse(); + bus_field_info.append(&mut v); + let n32: u32; + if s.dimensions.len() > 0 { + n32 = (s.dimensions.len() - 1) as u32; + } else { + n32 = 0; + } + // println!("dims-1: {}",n32); + let mut v: Vec = n32.to_be_bytes().to_vec(); + v.reverse(); + bus_field_info.append(&mut v); + for i in 1..s.dimensions.len() { + // println!("dims {}: {}",i,s.lengths[i]); + let pos = s.dimensions[i] as u32; + let mut v: Vec = pos.to_be_bytes().to_vec(); + v.reverse(); + bus_field_info.append(&mut v); + } + let s32 = s.size as u32; + let mut v: Vec = s32.to_be_bytes().to_vec(); + v.reverse(); + bus_field_info.append(&mut v); + let b32 = if let Some(value) = s.bus_id { + value as u32 + } else { + 0 as u32 + }; + let mut v = b32.to_be_bytes().to_vec(); + v.reverse(); + bus_field_info.append(&mut v); + } + } + bus_field_info +} + // in main fix one to 1 /* @@ -651,8 +735,11 @@ pub fn generate_dat_file(dat_file: &mut dyn Write, producer: &CProducer) -> std: //dfile.write_all(&p)?; //dfile.flush()?; +//<<<<<<< HEAD +//======= let aux = producer.get_main_input_list(); let map = generate_hash_map(&aux,producer.get_input_hash_map_entry_size()); +//>>>>>>> 9f3da35a8ac3107190f8c85c8cf3ea1a0f8780a4 let hashmap = generate_dat_from_hash_map(&map); //bytes u64 --> u64 //let hml = producer.get_input_hash_map_entry_size() as u32; //dfile.write_all(&hml.to_be_bytes())?; @@ -670,6 +757,11 @@ pub fn generate_dat_file(dat_file: &mut dyn Write, producer: &CProducer) -> std: //dfile.write_all(&ioml.to_be_bytes())?; let iomap = generate_dat_io_signals_info(&producer, producer.get_io_map()); dat_file.write_all(&iomap)?; + if producer.get_io_map().len() > 0 { + //otherwise it is not used + let fieldmap = generate_dat_bus_field_info(&producer, producer.get_busid_field_info()); + dat_file.write_all(&fieldmap)?; + } /* let ml = producer.get_message_list(); let mll = ml.len() as u64; @@ -681,7 +773,7 @@ pub fn generate_dat_file(dat_file: &mut dyn Write, producer: &CProducer) -> std: dfile.write_all(m)?; dfile.flush()?; } - */ + */ dat_file.flush()?; Ok(()) } @@ -979,6 +1071,7 @@ pub fn generate_c_file(name: String, producer: &CProducer) -> std::io::Result<() producer.get_field_constant_list().len() )); code.push(format!("uint get_size_of_io_map() {{return {};}}\n", producer.get_io_map().len())); + code.push(format!("uint get_size_of_bus_field_map() {{return {};}}\n", producer.get_busid_field_info().len())); // let mut ml_def = generate_message_list_def(producer, producer.get_message_list()); // code.append(&mut ml_def); diff --git a/code_producers/src/c_elements/common/calcwit.cpp b/code_producers/src/c_elements/common/calcwit.cpp index c39d50389..7abb459f8 100644 --- a/code_producers/src/c_elements/common/calcwit.cpp +++ b/code_producers/src/c_elements/common/calcwit.cpp @@ -35,6 +35,7 @@ Circom_CalcWit::Circom_CalcWit (Circom_Circuit *aCircuit, uint maxTh) { componentMemory = new Circom_Component[get_number_of_components()]; circuitConstants = circuit ->circuitConstants; templateInsId2IOSignalInfo = circuit -> templateInsId2IOSignalInfo; + busInsId2FieldInfo = circuit -> busInsId2FieldInfo; maxThread = maxTh; diff --git a/code_producers/src/c_elements/common/calcwit.hpp b/code_producers/src/c_elements/common/calcwit.hpp index 363de21d1..1cee1e7de 100644 --- a/code_producers/src/c_elements/common/calcwit.hpp +++ b/code_producers/src/c_elements/common/calcwit.hpp @@ -26,7 +26,8 @@ class Circom_CalcWit { FrElement *signalValues; Circom_Component* componentMemory; FrElement* circuitConstants; - std::map templateInsId2IOSignalInfo; + std::map templateInsId2IOSignalInfo; + IOFieldDefPair* busInsId2FieldInfo; std::string* listOfTemplateMessages; // parallelism diff --git a/code_producers/src/c_elements/common/circom.hpp b/code_producers/src/c_elements/common/circom.hpp index eabc686f6..3281a621f 100644 --- a/code_producers/src/c_elements/common/circom.hpp +++ b/code_producers/src/c_elements/common/circom.hpp @@ -20,15 +20,17 @@ struct __attribute__((__packed__)) HashSignalInfo { u64 signalsize; }; -struct IODef { +struct IOFieldDef { u32 offset; u32 len; u32 *lengths; + u32 size; + u32 busId; }; -struct IODefPair { +struct IOFieldDefPair { u32 len; - IODef* defs; + IOFieldDef* defs; }; struct Circom_Circuit { @@ -36,7 +38,8 @@ struct Circom_Circuit { HashSignalInfo* InputHashMap; u64* witness2SignalList; FrElement* circuitConstants; - std::map templateInsId2IOSignalInfo; + std::map templateInsId2IOSignalInfo; + IOFieldDefPair* busInsId2FieldInfo; }; @@ -81,5 +84,6 @@ uint get_size_of_input_hashmap(); uint get_size_of_witness(); uint get_size_of_constants(); uint get_size_of_io_map(); +uint get_size_of_bus_field_map(); #endif // __CIRCOM_H diff --git a/code_producers/src/c_elements/common/main.cpp b/code_producers/src/c_elements/common/main.cpp index 18d846c91..eff4a0808 100644 --- a/code_producers/src/c_elements/common/main.cpp +++ b/code_producers/src/c_elements/common/main.cpp @@ -54,7 +54,8 @@ Circom_Circuit* loadCircuit(std::string const &datFileName) { memcpy((void *)(circuit->circuitConstants), (void *)(bdata+inisize), dsize); } - std::map templateInsId2IOSignalInfo1; + std::map templateInsId2IOSignalInfo1; + IOFieldDefPair* busInsId2FieldInfo1; if (get_size_of_io_map()>0) { u32 index[get_size_of_io_map()]; inisize += dsize; @@ -66,12 +67,11 @@ Circom_Circuit* loadCircuit(std::string const &datFileName) { u32 dataiomap[(sb.st_size-inisize)/sizeof(u32)]; memcpy((void *)dataiomap, (void *)(bdata+inisize), sb.st_size-inisize); u32* pu32 = dataiomap; - for (int i = 0; i < get_size_of_io_map(); i++) { u32 n = *pu32; - IODefPair p; + IOFieldDefPair p; p.len = n; - IODef defs[n]; + IOFieldDef defs[n]; pu32 += 1; for (u32 j = 0; j templateInsId2IOSignalInfo = move(templateInsId2IOSignalInfo1); - + circuit->busInsId2FieldInfo = busInsId2FieldInfo1; + munmap(bdata, sb.st_size); return circuit; @@ -159,11 +187,67 @@ void json2FrElements (json val, std::vector & vval){ } } +json::value_t check_type(std::string prefix, json in){ + if (not in.is_array()) { + return in.type(); + } else { + if (in.size() == 0) return json::value_t::null; + json::value_t t = check_type(prefix, in[0]); + for (uint i = 1; i < in.size(); i++) { + if (t != check_type(prefix, in[i])) { + fprintf(stderr, "Types are not the same in the the key %s\n",prefix.c_str()); + assert(false); + } + } + return t; + } +} + +void qualify_input(std::string prefix, json &in, json &in1); + +void qualify_input_list(std::string prefix, json &in, json &in1){ + if (in.is_array()) { + for (uint i = 0; i 0) { + json::value_t t = check_type(prefix,in); + if (t == json::value_t::object) { + qualify_input_list(prefix,in,in1); + } else { + in1[prefix] = in; + } + } else { + in1[prefix] = in; + } + } else if (in.is_object()) { + for (json::iterator it = in.begin(); it != in.end(); ++it) { + std::string new_prefix = prefix.length() == 0 ? it.key() : prefix + "." + it.key(); + qualify_input(new_prefix,it.value(),in1); + } + } else { + in1[prefix] = in; + } +} void loadJson(Circom_CalcWit *ctx, std::string filename) { std::ifstream inStream(filename); + json jin; + inStream >> jin; json j; - inStream >> j; + + //std::cout << jin << std::endl; + std::string prefix = ""; + qualify_input(prefix, jin, j); + //std::cout << j << std::endl; u64 nItems = j.size(); // printf("Items : %llu\n",nItems); diff --git a/code_producers/src/c_elements/mod.rs b/code_producers/src/c_elements/mod.rs index ad4f0c221..ddc811c6d 100644 --- a/code_producers/src/c_elements/mod.rs +++ b/code_producers/src/c_elements/mod.rs @@ -30,6 +30,10 @@ pub struct CProducer { pub patch_version: usize, name_tag: String, string_table: Vec, + //New for buses + pub num_of_bus_instances: usize, //total number of different bus instances + //pub size_of_bus_fields: usize, //total number of fields in all differen bus intances + pub busid_field_info: FieldMap, //for every busId (0..num-1) provides de offset, size, dimensions and busId of each field (0..n-1) in it } impl Default for CProducer { @@ -38,26 +42,27 @@ impl Default for CProducer { my_map.insert( 0, vec![ - IODef { code: 0, offset: 0, lengths: [2, 3].to_vec() }, - IODef { code: 1, offset: 6, lengths: [].to_vec() }, - IODef { code: 2, offset: 7, lengths: [2].to_vec() }, + IODef { code: 0, offset: 0, lengths: [2, 3].to_vec(), size: 6, bus_id:None }, + IODef { code: 1, offset: 6, lengths: [].to_vec(), size: 1, bus_id:None }, + IODef { code: 2, offset: 7, lengths: [2].to_vec(), size: 2, bus_id:None }, ], ); my_map.insert( 1, vec![ - IODef { code: 0, offset: 0, lengths: [3].to_vec() }, - IODef { code: 1, offset: 3, lengths: [4, 8, 6].to_vec() }, + IODef { code: 0, offset: 0, lengths: [3].to_vec(), size: 3, bus_id:None }, + IODef { code: 1, offset: 3, lengths: [4, 8, 6].to_vec(), size: 192, bus_id:None }, ], ); my_map.insert( 2, vec![ - IODef { code: 0, offset: 0, lengths: [].to_vec() }, - IODef { code: 1, offset: 1, lengths: [4].to_vec() }, - IODef { code: 2, offset: 5, lengths: [2, 6].to_vec() }, + IODef { code: 0, offset: 0, lengths: [].to_vec(), size: 1, bus_id:None }, + IODef { code: 1, offset: 1, lengths: [4].to_vec(), size: 4, bus_id:None }, + IODef { code: 2, offset: 5, lengths: [2, 6].to_vec(), size: 12, bus_id:None }, ], ); + CProducer { main_header: "Main_0".to_string(), main_is_parallel: false, @@ -68,7 +73,22 @@ impl Default for CProducer { prime_str: "bn128".to_string(), number_of_main_outputs: 1, number_of_main_inputs: 2, - main_input_list: [("in1".to_string(), 2, 1), ("in2".to_string(), 3, 1)].to_vec(), //[].to_vec(), + main_input_list: [ + InputInfo{ + name:"in1".to_string(), + size:1, + dimensions: Vec::new(), + start: 2, + bus_id: None + }, + InputInfo{ + name:"in2".to_string(), + size:1, + dimensions: Vec::new(), + start: 3, + bus_id: None + }, + ].to_vec(), signals_in_witness: 20, witness_to_signal_list: [ 0, 1, 2, 3, 4, 5, 6, 12, 16, 19, 24, 27, 33, 42, 46, 50, 51, 65, 78, 79, @@ -96,6 +116,10 @@ impl Default for CProducer { patch_version: 0, name_tag: "name".to_string(), string_table: Vec::new(), + //New for buses + num_of_bus_instances: 0, +// size_of_bus_fields: 0, + busid_field_info: Vec::new(), } } } @@ -134,9 +158,12 @@ impl CProducer { pub fn get_main_input_list(&self) -> &InputList { &self.main_input_list } +//<<<<<<< HEAD +//======= pub fn get_input_hash_map_entry_size(&self) -> usize { std::cmp::max(usize::pow(2,(self.main_input_list.len() as f32).log2().ceil() as u32),256) } +//>>>>>>> 9f3da35a8ac3107190f8c85c8cf3ea1a0f8780a4 pub fn get_number_of_witness(&self) -> usize { self.signals_in_witness } @@ -178,4 +205,14 @@ impl CProducer { pub fn set_string_table(&mut self, string_table: Vec) { self.string_table = string_table; } + + //New for buses + pub fn get_number_of_bus_instances(&self) -> usize { + self.num_of_bus_instances + } + + pub fn get_busid_field_info(&self) -> &FieldMap { + &self.busid_field_info + } + // end } diff --git a/code_producers/src/components/mod.rs b/code_producers/src/components/mod.rs index 3c1dcc5cc..211041254 100644 --- a/code_producers/src/components/mod.rs +++ b/code_producers/src/components/mod.rs @@ -5,10 +5,32 @@ pub struct IODef { pub code: usize, pub offset: usize, pub lengths: Vec, + pub size: usize, + pub bus_id: Option } -// It is an array that contains (name, start position, size) -pub type InputList = Vec<(String, usize, usize)>; +// Previously an array that contains, now struct (name, start position, size, bus_id (if any)) +#[derive(Clone)] +pub struct InputInfo{ + pub name: String, + pub dimensions: Vec, + pub start: usize, + pub size: usize, //full size (not only the content if array) + pub bus_id: Option +} + +#[derive(Default, Clone)] +pub struct FieldData{ + pub dimensions: Vec, + pub size: usize, // it is only the size of the content if array + pub offset: usize, + pub bus_id: Option, + pub name: String +} + +pub type FieldMap = Vec>; + +pub type InputList = Vec; pub type TemplateList = Vec; pub struct InfoParallel{ pub name: String, diff --git a/code_producers/src/lib.rs b/code_producers/src/lib.rs index 8d77960cf..46fd883c6 100644 --- a/code_producers/src/lib.rs +++ b/code_producers/src/lib.rs @@ -4,3 +4,4 @@ pub mod c_elements; pub mod wasm_elements; pub mod components; + diff --git a/code_producers/src/wasm_elements/common/generate_witness.js b/code_producers/src/wasm_elements/common/generate_witness.js index eabb86e58..2c6656d6d 100755 --- a/code_producers/src/wasm_elements/common/generate_witness.js +++ b/code_producers/src/wasm_elements/common/generate_witness.js @@ -8,10 +8,11 @@ if (process.argv.length != 5) { const buffer = readFileSync(process.argv[2]); wc(buffer).then(async witnessCalculator => { - // const w= await witnessCalculator.calculateWitness(input,0); - // for (let i=0; i< w.length; i++){ - // console.log(w[i]); - // } + const w= await witnessCalculator.calculateWitness(input,0); + /* + for (let i=0; i< w.length; i++){ + console.log(w[i]); + }*/ const buff= await witnessCalculator.calculateWTNSBin(input,0); writeFile(process.argv[4], buff, function(err) { if (err) throw err; diff --git a/code_producers/src/wasm_elements/common/witness_calculator.js b/code_producers/src/wasm_elements/common/witness_calculator.js old mode 100755 new mode 100644 index 20e6e20ad..1560c9a39 --- a/code_producers/src/wasm_elements/common/witness_calculator.js +++ b/code_producers/src/wasm_elements/common/witness_calculator.js @@ -18,6 +18,9 @@ module.exports = async function builder(code, options) { const instance = await WebAssembly.instantiate(wasmModule, { runtime: { + printDebug : function(value) { + console.log("printDebug:",value); + }, exceptionHandler : function(code) { let err; if (code == 1) { @@ -128,9 +131,14 @@ class WitnessCalculator { return this.instance.exports.getVersion(); } - async _doCalculateWitness(input, sanityCheck) { + async _doCalculateWitness(input_orig, sanityCheck) { //input is assumed to be a map from signals to arrays of bigints this.instance.exports.init((this.sanityCheck || sanityCheck) ? 1 : 0); + let prefix = ""; + var input = new Object(); + //console.log("Input: ", input_orig); + qualify_input(prefix,input_orig,input); + //console.log("Input after: ",input); const keys = Object.keys(input); var input_counter = 0; keys.forEach( (k) => { @@ -171,7 +179,6 @@ class WitnessCalculator { async calculateWitness(input, sanityCheck) { const w = []; - await this._doCalculateWitness(input, sanityCheck); for (let i=0; i 0) { + let t = typeof a[0]; + for (let i = 1; i { + let new_prefix = prefix == ""? k : prefix + "." + k; + qualify_input(new_prefix,input[k],input1); + }); + } else { + input1[prefix] = input; + } +} + function toArray32(rem,size) { const res = []; //new Uint32Array(size); //has no unshift const radix = BigInt(0x100000000); diff --git a/code_producers/src/wasm_elements/mod.rs b/code_producers/src/wasm_elements/mod.rs index 1e6f9c3ce..58d49879e 100644 --- a/code_producers/src/wasm_elements/mod.rs +++ b/code_producers/src/wasm_elements/mod.rs @@ -36,6 +36,7 @@ pub struct WASMProducer { signal_offset_tag: String, signal_start_tag: String, sub_cmp_tag: String, + sub_cmp_src_tag: String, sub_cmp_load_tag: String, io_info_tag: String, result_address_tag: String, @@ -57,6 +58,10 @@ pub struct WASMProducer { create_loop_counter_tag: String, merror_tag: String, string_table: Vec, + //New for buses + pub num_of_bus_instances: usize, //total number of different bus instances +// pub size_of_bus_fields: usize, //total number of fields in all differen bus intances ??? + pub busid_field_info: FieldMap, //for every busId (0..num-1) provides de offset, the dimensions and size of each field (0..n-1) in it } impl Default for WASMProducer { @@ -76,7 +81,22 @@ impl Default for WASMProducer { size_32_shift: 5, number_of_main_outputs: 0, //2, number_of_main_inputs: 0, // 4, - main_input_list: [("in1".to_string(), 1, 1), ("in2".to_string(), 2, 1)].to_vec(), //("inpair".to_string(),2), + main_input_list: [ + InputInfo{ + name:"in1".to_string(), + size:1, + dimensions: Vec::new(), + start: 1, + bus_id: None + }, + InputInfo{ + name:"in2".to_string(), + size:1, + dimensions: Vec::new(), + start: 2, + bus_id: None + } + ].to_vec(), signals_in_witness: 0, //20, witness_to_signal_list: [].to_vec(), //[0,1,2,3,4,5,6,12,16,19,24,27,33,42,46,50,51,65,78,79].to_vec(), message_list: [].to_vec(), //["Main".to_string(),"Hola Herme".to_string(),"Hola Albert".to_string()].to_vec(), @@ -98,6 +118,7 @@ impl Default for WASMProducer { signal_offset_tag: "$signaloffset".to_string(), signal_start_tag: "$signalstart".to_string(), sub_cmp_tag: "$subcmp".to_string(), + sub_cmp_src_tag: "$subcmpsrc".to_string(), sub_cmp_load_tag: "$subcmpload".to_string(), io_info_tag: "$ioinfo".to_string(), result_address_tag: "$resultaddress".to_string(), @@ -119,7 +140,11 @@ impl Default for WASMProducer { create_loop_counter_tag: "$createloopcounter".to_string(), merror_tag: "$merror".to_string(), string_table: Vec::new(), - } + //New for buses + num_of_bus_instances: 0, +// size_of_bus_fields: 0, + busid_field_info: Vec::new(), + } } } @@ -179,8 +204,11 @@ impl WASMProducer { pub fn get_main_input_list(&self) -> &InputList { &self.main_input_list } +//HEAD +//======= pub fn get_input_hash_map_entry_size(&self) -> usize { std::cmp::max(usize::pow(2,(self.main_input_list.len() as f32).log2().ceil() as u32),256) +//>>>>>>> 9f3da35a8ac3107190f8c85c8cf3ea1a0f8780a4 } pub fn get_number_of_witness(&self) -> usize { self.signals_in_witness @@ -238,16 +266,55 @@ impl WASMProducer { let mut n = 0; for (_c, v) in &self.io_map { for s in v { - // since we take offset and all lengths but last one + // we take always offset, and size and all lengths but last one if len !=0, if s.lengths.len() == 0 { n += 1; } else { - n += s.lengths.len(); + n += s.lengths.len() + 1; } + // we take the bus_id if it has type bus + if let Some(_) = &s.bus_id { + n += 1; + } } } n * 4 } + //New for buses + pub fn get_number_of_bus_instances(&self) -> usize { + self.num_of_bus_instances + } + + pub fn get_number_of_bus_fields(&self) -> usize { + let mut n = 0; + for v in &self.busid_field_info { + n += v.len(); + } + n + } + + pub fn get_size_of_bus_info(&self) -> usize { + let mut n = 0; + for v in &self.busid_field_info { + for s in v { + // since we take offset, busid (if it is) and all lengths but first one and size if not zero + if s.dimensions.len() == 0 { + n += 1; + } else { + n += s.dimensions.len() + 1; + } + if let Some(_) = &s.bus_id { + n += 1; + } + } + } + n * 4 + } + + pub fn get_busid_field_info(&self) -> &FieldMap { + &self.busid_field_info + } + // end pub fn get_message_list(&self) -> &MessageList { &self.message_list } @@ -311,8 +378,21 @@ impl WASMProducer { let b = self.get_number_of_io_signals() * 4; a + b } + pub fn get_bus_instance_to_field_start(&self) -> usize { + self.get_io_signals_info_start() + self.get_io_signals_info_size() + } + pub fn get_field_to_info_start(&self) -> usize { + let a = self.get_bus_instance_to_field_start(); + let b = self.get_number_of_bus_instances() * 4; + a + b + } + pub fn get_field_info_start(&self) -> usize { + let a = self.get_field_to_info_start(); + let b = self.get_number_of_bus_fields() * 4; + a + b + } pub fn get_message_buffer_counter_position(&self) -> usize { - self.get_io_signals_info_start() + self.get_io_signals_info_size() + self.get_field_info_start() + self.get_size_of_bus_info() } pub fn get_message_buffer_start(&self) -> usize { self.get_message_buffer_counter_position() + 4 @@ -346,6 +426,9 @@ impl WASMProducer { pub fn get_sub_cmp_tag(&self) -> &str { &self.sub_cmp_tag } + pub fn get_sub_cmp_src_tag(&self) -> &str { + &self.sub_cmp_src_tag + } pub fn get_sub_cmp_load_tag(&self) -> &str { &self.sub_cmp_load_tag } diff --git a/code_producers/src/wasm_elements/wasm_code_generator.rs b/code_producers/src/wasm_elements/wasm_code_generator.rs index a490719fa..c070ee482 100644 --- a/code_producers/src/wasm_elements/wasm_code_generator.rs +++ b/code_producers/src/wasm_elements/wasm_code_generator.rs @@ -147,6 +147,12 @@ pub fn gt32_u() -> WasmInstruction { pub fn ge32_u() -> WasmInstruction { "i32.ge_u".to_string() } +pub fn lt32_u() -> WasmInstruction { + "i32.lt_u".to_string() +} +pub fn le32_u() -> WasmInstruction { + "i32.le_u".to_string() +} pub fn eq32() -> WasmInstruction { "i32.eq".to_string() } @@ -187,6 +193,27 @@ pub fn add_return() -> WasmInstruction { "return".to_string() } +pub fn create_if_selection( + values: &Vec<(usize, usize)>, + local: &str +) -> Vec { + let mut instructions = vec![]; + for i in 0..values.len() { + instructions.push(get_local(local)); + instructions.push(set_constant(&values[i].0.to_string())); //Add id in list + instructions.push(eq32()); + instructions.push(format!("{} (result i32)", add_if())); + instructions.push(set_constant(&values[i].1.to_string())); //Add corresponding size in list + instructions.push(add_else()); + } + instructions.push(set_constant("0")); //default o complete the last else + for _i in 0..values.len() { + instructions.push(add_end()); + } + instructions +} + + // ----- exception codes and other constants ----------------- pub fn default_memory_for_stack_kib() -> usize { @@ -226,16 +253,16 @@ pub fn get_initial_size_of_memory(producer: &WASMProducer) -> usize { //------------------- generate all kinds of Data ------------------ -pub fn generate_hash_map(signal_name_list: &Vec<(String, usize, usize)>, size: usize) -> Vec<(u64, usize, usize)> { +pub fn generate_hash_map(signal_name_list: &Vec, size: usize) -> Vec<(u64, usize, usize)> { assert!(signal_name_list.len() <= size); let mut hash_map = vec![(0, 0, 0); size]; for i in 0..signal_name_list.len() { - let h = hasher(&signal_name_list[i].0); + let h = hasher(&signal_name_list[i].name); let mut p = h as usize % size; while hash_map[p].1 != 0 { p = (p + 1) % size; } - hash_map[p] = (h, signal_name_list[i].1, signal_name_list[i].2); + hash_map[p] = (h, signal_name_list[i].start, signal_name_list[i].size); } hash_map } @@ -289,12 +316,15 @@ pub fn generate_data_io_signals_to_info( for s in value { assert_eq!(s.code, n); io_signals.push_str(&&wasm_hexa(4, &BigInt::from(pos))); - //do not store code and the first one of lengths - if s.lengths.len() == 0 { + //do not store code and the first one of lengths (offset + size + length-1(if >0) + if s.lengths.len() == 0 { //only offset pos += 4; - } else { - pos += s.lengths.len() * 4; + } else { // offest + length -1 + size + pos += s.lengths.len() * 4 + 4; } + if let Some(_) = s.bus_id { + pos += 4; + } n += 1; } } @@ -309,18 +339,37 @@ pub fn generate_data_io_signals_info( io_map: &TemplateInstanceIOMap, ) -> String { let mut io_signals_info = "".to_string(); - for c in 0..producer.get_number_of_components() { + for c in 0..producer.get_number_of_template_instances() { match io_map.get(&c) { Some(value) => { - for s in value { + //println!("Template Instance: {}", c); + for s in value { // add the actual offset in memory, taking into account the size of field nums + //println!("Offset: {}", s.offset); io_signals_info.push_str(&&wasm_hexa( 4, &BigInt::from(s.offset * producer.get_size_32_bits_in_memory() * 4), )); - for i in 1..s.lengths.len() { - io_signals_info.push_str(&&wasm_hexa(4, &BigInt::from(s.lengths[i]))); - } + //println!("Length: {}", s.lengths.len()); + if s.lengths.len() > 0 { // if it is an array + // add the dimensions except the first one + for i in 1..s.lengths.len() { + //println!("Index: {}, {}", i, s.lengths[i]); + io_signals_info.push_str(&&wasm_hexa(4, &BigInt::from(s.lengths[i]))); + } + // add the actual size of the elements + //println!("Size: {}", s.size); + io_signals_info.push_str(&&wasm_hexa( + 4, + &BigInt::from(s.size), + //&BigInt::from(s.size * producer.get_size_32_bits_in_memory() * 4), + )); + } + // add the busid if it is a bus + if let Some(value) = s.bus_id { + //println!("Bus_id: {}", value); + io_signals_info.push_str(&&wasm_hexa(4, &BigInt::from(value))); + } } } None => (), @@ -329,6 +378,82 @@ pub fn generate_data_io_signals_info( io_signals_info } + +pub fn generate_data_bus_instance_to_field( + producer: &WASMProducer, + field_map: &FieldMap, +) -> String { + let mut field_map_data = "".to_string(); + let mut s = producer.get_field_to_info_start(); + for c in 0..producer.get_number_of_bus_instances() { + field_map_data.push_str(&&wasm_hexa(4, &BigInt::from(s))); + s += field_map[c].len() * 4; + } + field_map_data +} + +pub fn generate_data_field_to_info( + producer: &WASMProducer, + field_map: &FieldMap, +) -> String { + let mut bus_fields = "".to_string(); + let mut pos = producer.get_field_info_start(); + for c in 0..producer.get_number_of_bus_instances() { + for s in &field_map[c] { + bus_fields.push_str(&&wasm_hexa(4, &BigInt::from(pos))); + //do not store the first one of lengths + if s.dimensions.len() == 0 { + pos += 4; + } else { + pos += s.dimensions.len() * 4 + 4; + } + if let Some(_) = s.bus_id { + pos += 4; + } + } + } + bus_fields +} + +pub fn generate_data_field_info( + producer: &WASMProducer, + field_map: &FieldMap, +) -> String { + let mut field_info = "".to_string(); + for c in 0..producer.get_number_of_bus_instances() { + //println!("Bus Instance: {}", c); + for s in &field_map[c] { + // add the actual offset in memory, taking into account the size of field nums + //println!("Offset: {}", s.offset); + field_info.push_str(&&wasm_hexa( + 4, + &BigInt::from(s.offset * producer.get_size_32_bits_in_memory() * 4), + )); + //println!("Length: {}", s.dimensions.len()); + if s.dimensions.len() > 0 { // if it is an array + // add all dimensions but first one + for i in 1..s.dimensions.len() { + //println!("Index: {}, {}", i, s.dimensions[i]); + field_info.push_str(&&wasm_hexa(4, &BigInt::from(s.dimensions[i]))); + } + // add the actual size in memory, if array + //println!("Size: {}", s.size); + field_info.push_str(&&wasm_hexa( + 4, + &BigInt::from(s.size), + //&BigInt::from(s.size * producer.get_size_32_bits_in_memory() * 4), + )); + } + // add the busid if it contains buses + if let Some(value) = s.bus_id { + //println!("Bus_id: {}", value); + field_info.push_str(&&wasm_hexa(4, &BigInt::from(value))); + } + } + } + field_info +} + pub fn generate_data_constants(producer: &WASMProducer, constant_list: &Vec) -> String { let mut constant_list_data = "".to_string(); // For short/long form @@ -505,6 +630,10 @@ pub fn generate_imports_list() -> Vec { "(import \"runtime\" \"showSharedRWMemory\" (func $showSharedRWMemory (type $_t_void)))" .to_string(), ); + imports.push( + "(import \"runtime\" \"printDebug\" (func $printDebug (type $_t_i32)))" + .to_string(), + ); imports } @@ -567,35 +696,60 @@ pub fn generate_data_list(producer: &WASMProducer) -> Vec { "\\00\\00\\00\\00\\00\\00\\00\\80" )); let map = generate_hash_map(&producer.get_main_input_list(),producer.get_input_hash_map_entry_size()); + wdata.push(format!(";; hash_map")); wdata.push(format!( "(data (i32.const {}) \"{}\")", producer.get_input_signals_hashmap_start(), generate_data_from_hash_map(&map) )); let s = generate_data_witness_to_signal_list(producer.get_witness_to_signal_list()); + wdata.push(format!(";; witness_to_signal_list")); wdata.push(format!( "(data (i32.const {}) \"{}\")", producer.get_witness_signal_id_list_start(), s )); + wdata.push(format!(";; signal memory")); wdata.push(format!("(data (i32.const {}) \"{}{}\")",producer.get_signal_memory_start(),"\\00\\00\\00\\00\\00\\00\\00\\80",wasm_hexa(producer.get_size_32_bit()*4, &BigInt::from(1)))); //setting 'one' as long normal 1 + wdata.push(format!(";; template_instance_to_io_signal")); wdata.push(format!( "(data (i32.const {}) \"{}\")", producer.get_template_instance_to_io_signal_start(), generate_data_template_instance_to_io(&producer, producer.get_io_map()) )); + wdata.push(format!(";; io_signals_to_info")); wdata.push(format!( "(data (i32.const {}) \"{}\")", producer.get_io_signals_to_info_start(), generate_data_io_signals_to_info(&producer, producer.get_io_map()) )); + wdata.push(format!(";; io_signals_info")); wdata.push(format!( "(data (i32.const {}) \"{}\")", producer.get_io_signals_info_start(), generate_data_io_signals_info(&producer, producer.get_io_map()) )); + wdata.push(format!(";; bus_instance_to_field")); + wdata.push(format!( + "(data (i32.const {}) \"{}\")", + producer.get_bus_instance_to_field_start(), + generate_data_bus_instance_to_field(&producer, producer.get_busid_field_info()) + )); + wdata.push(format!(";; field_to_info")); + wdata.push(format!( + "(data (i32.const {}) \"{}\")", + producer.get_field_to_info_start(), + generate_data_field_to_info(&producer, producer.get_busid_field_info()) + )); + wdata.push(format!(";; field_info")); + wdata.push(format!( + "(data (i32.const {}) \"{}\")", + producer.get_field_info_start(), + generate_data_field_info(&producer, producer.get_busid_field_info()) + )); let ml = producer.get_message_list(); let m = producer.get_message_list_start(); + wdata.push(format!(";; messages_in_bytes")); for i in 0..ml.len() { if ml[i].len() < producer.get_size_of_message_in_bytes() { wdata.push(format!( @@ -628,6 +782,7 @@ pub fn generate_data_list(producer: &WASMProducer) -> Vec { )); } } + wdata.push(format!(";; constants")); wdata.push(format!( "(data (i32.const {}) \"{}\")", producer.get_constant_numbers_start(), diff --git a/compiler/Cargo.toml b/compiler/Cargo.toml index 0e8ef09d3..a211d6b87 100644 --- a/compiler/Cargo.toml +++ b/compiler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "compiler" -version = "2.1.9" +version = "2.2.0" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/compiler/src/circuit_design/build.rs b/compiler/src/circuit_design/build.rs index 1e71090cc..6e1fe401b 100644 --- a/compiler/src/circuit_design/build.rs +++ b/compiler/src/circuit_design/build.rs @@ -14,9 +14,9 @@ fn matching_lengths_and_offsets(list: &InputOutputList) { let mut prev = 0; let mut offset = 0; for signal in list { - debug_assert_eq!(signal.offset, prev + offset); + //debug_assert_eq!(signal.offset, prev + offset); prev = signal.offset; - offset = signal.lengths.iter().fold(1, |p, c| p * (*c)); + offset = signal.lengths.iter().fold(signal.size, |p, c| p * (*c)); } } @@ -87,12 +87,13 @@ fn build_template_instances( message_id: tmp_id, params: Vec::new(), header: header.clone(), - signals: template.signals, + wires: template.wires, constants: instance_values, files: &c_info.file_library, triggers: template.triggers, clusters: template.clusters, functions: &c_info.functions, + buses: &c_info.buses, fresh_cmp_id: cmp_id, components: template.components, template_database: &c_info.template_database, @@ -152,7 +153,7 @@ fn build_function_instances( functions: &c_info.functions, params: params.clone(), fresh_cmp_id: 0, - signals: Vec::with_capacity(0), + wires: Vec::with_capacity(0), triggers: Vec::with_capacity(0), clusters: Vec::with_capacity(0), constants: Vec::with_capacity(0), @@ -162,6 +163,7 @@ fn build_function_instances( template_database: &c_info.template_database, string_table : string_table, signals_to_tags: BTreeMap::new(), + buses: &c_info.buses }; let mut function_info = FunctionCodeInfo { name, @@ -221,11 +223,19 @@ fn initialize_wasm_producer(vcp: &VCP, database: &TemplateDB, wat_flag:bool, ver producer.signals_in_witness = producer.witness_to_signal_list.len(); producer.number_of_main_inputs = vcp.templates[initial_node].number_of_inputs; producer.number_of_main_outputs = vcp.templates[initial_node].number_of_outputs; - producer.main_input_list = main_input_list(&vcp.templates[initial_node]); + + // add the info of the buses + ( + producer.num_of_bus_instances, + producer.busid_field_info + ) = get_info_buses(&vcp.buses); + + producer.main_input_list = main_input_list(&vcp.templates[initial_node],&producer.busid_field_info); producer.io_map = build_io_map(vcp, database); producer.template_instance_list = build_template_list(vcp); producer.field_tracking.clear(); producer.wat_flag = wat_flag; + (producer.major_version, producer.minor_version, producer.patch_version) = get_number_version(version); producer } @@ -255,23 +265,150 @@ fn initialize_c_producer(vcp: &VCP, database: &TemplateDB, version: &str) -> CPr producer.signals_in_witness = producer.witness_to_signal_list.len(); producer.number_of_main_inputs = vcp.templates[initial_node].number_of_inputs; producer.number_of_main_outputs = vcp.templates[initial_node].number_of_outputs; - producer.main_input_list = main_input_list(&vcp.templates[initial_node]); + // add the info of the buses + ( + producer.num_of_bus_instances, + producer.busid_field_info + ) = get_info_buses(&vcp.buses); + + producer.main_input_list = main_input_list(&vcp.templates[initial_node],&producer.busid_field_info); producer.io_map = build_io_map(vcp, database); producer.template_instance_list = build_template_list_parallel(vcp); producer.field_tracking.clear(); + (producer.major_version, producer.minor_version, producer.patch_version) = get_number_version(version); producer } -fn main_input_list(main: &TemplateInstance) -> InputList { +fn main_input_list(main: &TemplateInstance, buses: &FieldMap) -> InputList { use program_structure::ast::SignalType::*; + use crate::hir::very_concrete_program::Wire::*; + fn build_info_wire(wire: &Wire) -> InputInfo{ + match wire{ + TSignal(info) =>{ + InputInfo{ + name: info.name.clone(), + dimensions: info.lengths.clone(), + size: info.size, + start: info.dag_local_id, + bus_id: None + } + }, + TBus(info) =>{ + InputInfo{ + name: info.name.clone(), + dimensions: info.lengths.clone(), + size: info.size, + start: info.dag_local_id, + bus_id: Some(info.bus_id) + } + } + } + } + fn get_accesses(pos: usize, dims: &Vec) -> Vec<(String,usize)> { + if pos >= dims.len() { + vec![("".to_string(),0)] + } else { + let mut res: Vec<(String,usize)> = vec![]; + let res1 = get_accesses(pos+1, dims); + let mut elems:usize = 1; + let mut epos = pos + 1; + while epos < dims.len() { + elems *= dims[epos]; + epos += 1; + } + let mut jump = 0; + for i in 0..dims[pos] { + for j in 0..res1.len() { + let (a,s) = &res1[j]; + res.push((format!("[{}]{}",i,a),jump+s)); + } + jump += elems; + } + res + } + } + fn get_qualified_names (busid: usize, start: usize, prefix: String, buses: &FieldMap) -> InputList { + let mut buslist = vec![]; + //println!("BusId: {}", busid); + for io in &buses[busid] { + let name = format!("{}.{}",prefix.clone(),io.name); + let new_start = start + io.offset; + //print!("name: {}, start: {}", name, new_start); + if let Some(value) = io.bus_id { + let accesses = get_accesses(0,&io.dimensions); + //println!("accesses list: {:?}", accesses); + for (a,s) in &accesses { + let prefix = format!("{}{}",name.clone(),a); + let mut ios = get_qualified_names (value,new_start+s*io.size,prefix,buses); + buslist.append(&mut ios); + } + } + else { + //println!(""); + let mut total_size = io.size; + for i in &io.dimensions { + total_size *= i; + } + let ioinfo = { + InputInfo{ + name: name, + dimensions: io.dimensions.clone(), + size: total_size, + start: new_start, + bus_id: None + } }; + buslist.push(ioinfo); + } + } + buslist + } + pub fn get_main_input_list_with_qualifiers(buses: &FieldMap, input_list: &InputList) -> InputList { + let mut iolist = vec![]; + for io in input_list { + if let Some(value) = io.bus_id { + let mut elems:usize = 1; + for i in &io.dimensions { + elems *= i; + } + let size:usize = io.size/elems; + let accesses = get_accesses(0,&io.dimensions); + for (a,s) in &accesses { + let prefix = format!("{}{}",io.name.clone(),a); + let mut ios = get_qualified_names (value,io.start+s*size,prefix,buses); + iolist.append(&mut ios); + } + } + else { + iolist.push(io.clone()); + } + } + iolist + } let mut input_list = vec![]; - for s in &main.signals { - if s.xtype == Input { - input_list.push((s.name.clone(), s.dag_local_id, s.size())); + for s in &main.wires { + if s.xtype() == Input { + input_list.push(build_info_wire(s)); } } - input_list + let mut input_list_with_qualifiers = get_main_input_list_with_qualifiers(buses,&input_list); + //for io in &input_list_with_qualifiers { + // println!("Name: {}, Start: {}, Size: {}",io.name, io.start, io.size); + //} +// let input_list = producer.get_main_input_list(); + let mut id_to_info: HashMap = HashMap::new(); + for io in &input_list_with_qualifiers { + id_to_info.insert(io.name.clone(),(io.start, io.size)); + } + for io in input_list { + if id_to_info.contains_key(&io.name) { + let (st,sz) = id_to_info[&io.name]; + assert!(st == io.start && sz == io.size); + } else { + input_list_with_qualifiers.push(io.clone()); + } + } + input_list_with_qualifiers } fn build_template_list(vcp: &VCP) -> TemplateList { @@ -307,12 +444,19 @@ fn build_io_map(vcp: &VCP, database: &TemplateDB) -> TemplateInstanceIOMap { fn build_input_output_list(instance: &TemplateInstance, database: &TemplateDB) -> InputOutputList { use program_structure::ast::SignalType::*; let mut io_list = vec![]; - for s in &instance.signals { - if s.xtype != Intermediate { + for s in &instance.wires { + let mut total_array_size = 1; + for len in s.lengths(){ + total_array_size *= len; + } + let individual_size = s.size() / total_array_size; + if s.xtype() != Intermediate { let def = IODef { - code: TemplateDB::get_signal_id(database, &instance.template_name, &s.name), - offset: s.local_id, - lengths: s.lengths.clone(), + code: TemplateDB::get_signal_id(database, &instance.template_name, s.name()), + offset: s.local_id(), + lengths: s.lengths().clone(), + size: individual_size, + bus_id: s.bus_id() }; io_list.push(def); } @@ -324,7 +468,54 @@ fn build_input_output_list(instance: &TemplateInstance, database: &TemplateDB) - io_list } -fn write_main_inputs_log(vcp: &VCP) { +fn write_main_inputs_log_new(vcp: &VCP) { + use program_structure::ast::SignalType::*; + use std::fs::File; + use std::io::{BufWriter, Write}; + + fn write_signal(vcp: &VCP, name: &String, length: &Vec, bus_id: Option, writer: &mut BufWriter){ + let length = length.iter().fold(1, |acc, x| acc * x); + if bus_id.is_some(){ + let bus_info = &vcp.buses[bus_id.unwrap()]; + let fields = &bus_info.fields; + let msg = format!("{} {} {}\n", name, length, fields.len()); + writer.write_all(msg.as_bytes()).unwrap(); + for (name, info) in fields{ + write_signal( + vcp, + name, + &info.dimensions, + info.bus_id, + writer, + ) + } + + } else{ + let msg = format!("{} {} {}\n", name, length, 0); + writer.write_all(msg.as_bytes()).unwrap(); + } + + } + + + const INPUT_LOG: &str = "./log_input_signals_new.txt"; + let main = vcp.get_main_instance().unwrap(); + let mut writer = BufWriter::new(File::create(INPUT_LOG).unwrap()); + for signal in &main.wires { + if signal.xtype() == Input { + write_signal( + vcp, + signal.name(), + signal.lengths(), + signal.bus_id(), + &mut writer, + ) + } + writer.flush().unwrap(); + } +} + +fn write_main_inputs_log_old(vcp: &VCP) { use program_structure::ast::SignalType::*; use std::fs::File; use std::io::{BufWriter, Write}; @@ -332,9 +523,9 @@ fn write_main_inputs_log(vcp: &VCP) { const INPUT_LOG: &str = "./log_input_signals.txt"; let main = vcp.get_main_instance().unwrap(); let mut writer = BufWriter::new(File::create(INPUT_LOG).unwrap()); - for signal in &main.signals { - if signal.xtype == Input { - let name = format!("main.{}", &signal.name); + for signal in &main.wires { + if signal.xtype() == Input { + let name = format!("main.{}", &signal.name()); let length = signal.size(); let msg = format!("{} {}\n", name, length); writer.write_all(msg.as_bytes()).unwrap(); @@ -353,16 +544,45 @@ fn get_number_version(version: &str) -> (usize, usize, usize) { ) } +fn get_info_buses(buses: &Vec)->(usize, FieldMap){ + let mut n_buses = 0; + let mut bus_to_fields_data = Vec::new(); + for bus in buses{ + let mut field_data = vec![FieldData::default(); bus.fields.len()]; + for (name, field_info) in &bus.fields{ + let mut total_array_size = 1; + for len in &field_info.dimensions{ + total_array_size *= len; + } + let individual_size = field_info.size / total_array_size; + let data = FieldData{ + offset: field_info.offset, + size: individual_size, + dimensions: field_info.dimensions.clone(), + bus_id: field_info.bus_id, + name: name.clone() + }; + field_data[field_info.field_id] = data; + } + bus_to_fields_data.push(field_data); + n_buses += 1; + } + (n_buses, bus_to_fields_data) +} + struct CircuitInfo { file_library: FileLibrary, functions: HashMap>, template_database: TemplateDB, + buses: Vec } pub fn build_circuit(vcp: VCP, flag: CompilationFlags, version: &str) -> Circuit { use crate::ir_processing::set_arena_size_in_calls; if flag.main_inputs_log { - write_main_inputs_log(&vcp); + write_main_inputs_log_old(&vcp); + write_main_inputs_log_new(&vcp); + } let template_database = TemplateDB::build(&vcp.templates); let mut circuit = Circuit::default(); @@ -374,6 +594,7 @@ pub fn build_circuit(vcp: VCP, flag: CompilationFlags, version: &str) -> Circuit template_database, file_library: vcp.file_library, functions: vcp.quick_knowledge, + buses: vcp.buses }; let (field_tracker, string_table) = diff --git a/compiler/src/circuit_design/circuit.rs b/compiler/src/circuit_design/circuit.rs index 903fb9bec..8dee26286 100644 --- a/compiler/src/circuit_design/circuit.rs +++ b/compiler/src/circuit_design/circuit.rs @@ -360,6 +360,10 @@ impl WriteC for Circuit { "uint get_size_of_io_map() {{return {};}}\n", producer.get_io_map().len() )); + code.push(format!( + "uint get_size_of_bus_field_map() {{return {};}}\n", + producer.get_busid_field_info().len() + )); //code.append(&mut generate_message_list_def(producer, producer.get_message_list())); // Functions to release the memory @@ -479,6 +483,10 @@ impl WriteC for Circuit { "uint get_size_of_io_map() {{return {};}}\n", producer.get_io_map().len() )); + code.push(format!( + "uint get_size_of_bus_field_map() {{return {};}}\n", + producer.get_busid_field_info().len() + )); //code.append(&mut generate_message_list_def(producer, producer.get_message_list())); // Functions to release the memory @@ -527,22 +535,27 @@ impl WriteC for Circuit { // let start_msg = "printf(\"Starting...\\n\");".to_string(); // let end_msg = "printf(\"End\\n\");".to_string(); - let main_template_run = if producer.main_is_parallel{ - producer.main_header.clone() + "_run_parallel" + let run_call = if producer.number_of_main_inputs > 0{ + + let main_template_run = if producer.main_is_parallel{ + producer.main_header.clone() + "_run_parallel" + } else{ + producer.main_header.clone() + "_run" + }; + let mut run_args = vec![]; + // run_args.push(CTX_INDEX.to_string()); + run_args.push("0".to_string()); + run_args.push(CIRCOM_CALC_WIT.to_string()); + format!("{};", build_call(main_template_run, run_args.clone())) } else{ - producer.main_header.clone() + "_run" + "// no input signals, the creation will automatically execute".to_string() }; - let mut run_args = vec![]; - // run_args.push(CTX_INDEX.to_string()); - run_args.push("0".to_string()); - run_args.push(CIRCOM_CALC_WIT.to_string()); - let run_call = format!("{};", build_call(main_template_run, run_args.clone())); let main_run_body = vec![ctx_index, run_call]; - code_write = build_callable(run_circuit, run_circuit_args, main_run_body) + "\n"; + code_write = build_callable(run_circuit, run_circuit_args, main_run_body) + "\n"; writer.write_all(code_write.as_bytes()).map_err(|_| {})?; writer.flush().map_err(|_| {}) - + } } diff --git a/compiler/src/circuit_design/template.rs b/compiler/src/circuit_design/template.rs index 399ca43e8..7b9c4b687 100644 --- a/compiler/src/circuit_design/template.rs +++ b/compiler/src/circuit_design/template.rs @@ -47,7 +47,7 @@ impl WriteWasm for TemplateCodeInfo { instructions.push(set_constant(&producer.get_component_free_pos().to_string())); instructions.push(load32(None)); instructions.push(set_local(producer.get_offset_tag())); - // set component id + // set template id instructions.push(get_local(producer.get_offset_tag())); instructions.push(set_constant(&self.id.to_string())); instructions.push(store32(None)); @@ -99,6 +99,7 @@ impl WriteWasm for TemplateCodeInfo { instructions.push(format!(" (local {} i32)", producer.get_create_loop_offset_tag())); instructions.push(format!(" (local {} i32)", producer.get_create_loop_counter_tag())); instructions.push(format!(" (local {} i32)", producer.get_merror_tag())); + instructions.push(format!(" (local {} i32)", producer.get_result_size_tag())); // used when calling functions assigned to inputs of subcomponents let local_info_size_u32 = producer.get_local_info_size_u32(); // in the future we can add some info like pointer to run father or text father //set lvar (start of auxiliar memory for vars) instructions.push(set_constant("0")); diff --git a/compiler/src/hir/analysis_utilities.rs b/compiler/src/hir/analysis_utilities.rs index 5d685b15b..dc9f877f3 100644 --- a/compiler/src/hir/analysis_utilities.rs +++ b/compiler/src/hir/analysis_utilities.rs @@ -6,6 +6,14 @@ use std::collections::HashMap; pub type E = VarEnvironment; +pub struct InfoBus { + pub size: usize, + pub signals: HashMap, + pub buses: HashMap +} + + + pub struct GenericFunction { pub name: String, pub params_names: Vec, @@ -14,10 +22,11 @@ pub struct GenericFunction { } pub struct State { - pub external_signals: HashMap>, + pub external_signals: HashMap, HashMap)>, pub generic_functions: HashMap, pub vcf_collector: Vec, pub quick_knowledge: HashMap, + pub buses_info: HashMap } pub fn build_function_knowledge(program: ProgramArchive) -> State { @@ -39,25 +48,73 @@ pub fn build_function_knowledge(program: ProgramArchive) -> State { vcf_collector: Vec::new(), generic_functions, quick_knowledge: HashMap::new(), + buses_info: HashMap::new() } } -pub fn build_component_info(triggers: &Vec) -> HashMap> { +pub fn build_component_info(triggers: &Vec, buses_table: &Vec) -> + HashMap, HashMap)> { let mut external_signals = HashMap::new(); for trigger in triggers { let mut signals = HashMap::new(); - for s in &trigger.external_signals { - signals.insert(s.name.clone(), s.lengths.clone()); + let mut buses = HashMap::new(); + for s in &trigger.external_wires { + match s{ + Wire::TSignal(s) =>{ + signals.insert(s.name.clone(), s.lengths.clone()); + }, + Wire::TBus(s) =>{ + buses.insert(s.name.clone(), (s.lengths.clone(), build_single_bus_info(s.bus_id, buses_table))); + } + } } - let signals = match external_signals.remove(&trigger.component_name) { - None => signals, - Some(old) => max_vct(signals, old), + + let (signals, buses) = match external_signals.remove(&trigger.component_name){ + None => (signals, buses), + Some((old_signals, old_buses)) =>{ + (max_vct(signals, old_signals), max_vct_bus(buses, old_buses)) + } }; - external_signals.insert(trigger.component_name.clone(), signals); + external_signals.insert(trigger.component_name.clone(), (signals, buses)); } external_signals } + +pub fn build_buses_info(wires: &Vec, buses_table: &Vec) -> HashMap{ + + let mut info_buses = HashMap::new(); + for s in wires{ + if let Wire::TBus(bus) = s{ + info_buses.insert(bus.name.clone(), build_single_bus_info(bus.bus_id, buses_table)); + } + } + info_buses +} + +fn build_single_bus_info(bus_id: usize, buses_table: &Vec) -> InfoBus{ + let bus_instance = buses_table.get(bus_id).unwrap(); + let mut signals = HashMap::new(); + let mut buses = HashMap::new(); + + for (name,s) in &bus_instance.fields{ + if s.bus_id.is_none(){ // case signal + signals.insert( + name.clone(), + s.dimensions.clone() + ); + } else{ + let bus_id = s.bus_id.unwrap(); + buses.insert( + name.clone(), + (s.dimensions.clone(), build_single_bus_info(bus_id, buses_table)) + ); + } + } + + InfoBus{size: bus_instance.size, signals, buses} +} + fn max_vct(l: HashMap, mut r: HashMap) -> HashMap { let mut result = HashMap::new(); @@ -75,6 +132,30 @@ fn max_vct(l: HashMap, mut r: HashMap) -> HashMap, mut r: HashMap) -> HashMap { + fn compute_max_bus(l: InfoBus, r: InfoBus) ->InfoBus{ + let size = std::cmp::max(l.size, r.size); + let signals = max_vct(l.signals, r.signals); + let buses = max_vct_bus(l.buses, r.buses); + InfoBus{size, signals, buses} + } + let mut result = HashMap::new(); + + for (s, (tl, bl)) in l { + if r.contains_key(&s) { + let (tr, br) = r.remove(&s).unwrap(); + let tmax = std::cmp::max(tl, tr); + let bmax = compute_max_bus(bl, br); + result.insert(s, (tmax, bmax)); + } else{ + result.insert(s, (tl, bl)); + } + } + for (s, (tr, br)) in r{ + result.insert(s, (tr, br)); + } + result +} pub fn build_environment(constants: &[Argument], params: &[Param]) -> E { let mut environment = E::new(); for constant in constants { diff --git a/compiler/src/hir/component_preprocess.rs b/compiler/src/hir/component_preprocess.rs index 60362f04f..85f48a587 100644 --- a/compiler/src/hir/component_preprocess.rs +++ b/compiler/src/hir/component_preprocess.rs @@ -70,18 +70,30 @@ fn rm_init(stmt: &mut Statement) { use Statement::InitializationBlock; use VariableType::*; if let InitializationBlock { initializations, xtype, .. } = stmt { - if let Signal(..) = xtype { + + if let Signal(..) = xtype { let work = std::mem::take(initializations); for mut i in work { if i.is_substitution() { initializations.push(i); + } else if i.is_block(){ + rm_block(&mut i); + initializations.push(i); } - else if i.is_block(){ + } + } else if let Bus(..) = xtype{ + let work = std::mem::take(initializations); + for mut i in work { + if i.is_substitution() { + if !should_be_removed(&i) { + initializations.push(i); + } + } else if i.is_block(){ rm_block(&mut i); initializations.push(i); } } - } else { + }else { let filter = std::mem::take(initializations); for mut s in filter { rm_statement(&mut s); @@ -109,8 +121,10 @@ fn should_be_removed(stmt: &Statement) -> bool { use VariableType::*; if let InitializationBlock { xtype, .. } = stmt { Component == *xtype || AnonymousComponent == *xtype - } else if let Substitution { meta, .. } = stmt { - meta.get_type_knowledge().is_component() || meta.get_type_knowledge().is_tag() + } else if let Substitution { meta, rhe, .. } = stmt { + meta.get_type_knowledge().is_component() + || meta.get_type_knowledge().is_tag() + || rhe.is_bus_call() || rhe.is_bus_call_array() } else { false } diff --git a/compiler/src/hir/merger.rs b/compiler/src/hir/merger.rs index 4cb428b79..8ddf322a9 100644 --- a/compiler/src/hir/merger.rs +++ b/compiler/src/hir/merger.rs @@ -4,7 +4,7 @@ use super::sugar_cleaner; use super::very_concrete_program::*; use program_structure::ast::*; use program_structure::program_archive::ProgramArchive; -use num_traits::{ToPrimitive}; +use num_traits::ToPrimitive; pub fn run_preprocessing(vcp: &mut VCP, program_archive: ProgramArchive) { @@ -22,13 +22,14 @@ fn produce_vcf(vcp: &VCP, state: &mut State) { let code = &n.code; let constants = &n.header; let params = vec![]; - state.external_signals = build_component_info(&n.triggers); + state.external_signals = build_component_info(&n.triggers, &vcp.buses); + state.buses_info = build_buses_info(&n.wires, &vcp.buses); let mut env = build_environment(constants, ¶ms); produce_vcf_stmt(code, state, &mut env); } let mut index = 0; while index < state.vcf_collector.len() { - state.external_signals = build_component_info(&vec![]); + state.external_signals = build_component_info(&vec![], &vcp.buses); let mut env = build_environment(&vec![], &state.vcf_collector[index].params_types); let body = state.vcf_collector[index].body.clone(); produce_vcf_stmt(&body, state, &mut env); @@ -39,7 +40,8 @@ fn produce_vcf(vcp: &VCP, state: &mut State) { fn link_circuit(vcp: &mut VCP, state: &mut State) { for node in &mut vcp.templates { let mut env = build_environment(&node.header, &vec![]); - state.external_signals = build_component_info(&node.triggers); + state.external_signals = build_component_info(&node.triggers, &vcp.buses); + state.buses_info = build_buses_info(&node.wires, &vcp.buses); link_stmt(&mut node.code, state, &mut env); } let mut linked_vcf_collector = state.vcf_collector.clone(); @@ -117,6 +119,7 @@ fn produce_vcf_stmt(stmt: &Statement, state: &mut State, environment: &mut E) { } fn produce_vcf_expr(expr: &Expression, state: &mut State, environment: &E) { + if expr.is_infix() { produce_vcf_infix(expr, state, environment); } else if expr.is_prefix() { @@ -133,6 +136,8 @@ fn produce_vcf_expr(expr: &Expression, state: &mut State, environment: &E) { produce_vcf_array(expr, state, environment); } else if expr.is_parallel(){ produce_vcf_parallel(expr, state, environment); + } else if expr.is_bus_call(){ + produce_vcf_bus_call(expr, state, environment); } else { unreachable!(); } @@ -189,6 +194,7 @@ fn produce_vcf_substitution(stmt: &Statement, state: &mut State, environment: &E produce_vcf_expr(index, state, environment); } } + } else { unreachable!(); } @@ -280,6 +286,17 @@ fn produce_vcf_call(expr: &Expression, state: &mut State, environment: &E) { } } +fn produce_vcf_bus_call(expr: &Expression, state: &mut State, environment: &E) { + use Expression::BusCall; + if let BusCall { args, .. } = expr { + for arg in args { + produce_vcf_expr(arg, state, environment); + } + } else { + unreachable!(); + } +} + fn produce_vcf_variable(expr: &Expression, state: &mut State, environment: &E) { use Access::ArrayAccess; use Expression::Variable; @@ -498,6 +515,8 @@ fn link_expression(expr: &mut Expression, state: &State, env: &E) { link_prefix(expr, state, env); } else if expr.is_parallel(){ link_parallel(expr, state, env); + } else if expr.is_bus_call(){ + link_bus_call(expr, state, env); } else { unreachable!(); } @@ -521,6 +540,17 @@ fn link_call(expr: &mut Expression, state: &State, env: &E) { } } +fn link_bus_call(expr: &mut Expression, state: &State, env: &E) { + use Expression::BusCall; + if let BusCall { args, .. } = expr { + for arg in args.iter_mut() { + link_expression(arg, state, env); + } + } else { + unreachable!(); + } +} + fn link_array(expr: &mut Expression, state: &State, env: &E) { use Expression::{ArrayInLine, UniformArray}; if let ArrayInLine { values, .. } = expr { @@ -609,18 +639,53 @@ fn cast_type_variable(expr: &Expression, state: &State, environment: &E) -> VCT if let Variable { name, access, .. } = expr { let mut xtype = environment.get_variable(name).unwrap().clone(); xtype.reverse(); + let mut possible_bus_info = if state.buses_info.contains_key(name){ + Some(state.buses_info.get(name).unwrap()) + } else{ + None + }; for acc in access { match acc { Access::ArrayAccess(_) => { xtype.pop(); } Access::ComponentAccess(signal) => { - xtype = state.external_signals.get(name).unwrap().get(signal).unwrap().clone(); - xtype.reverse(); + if possible_bus_info.is_some(){ + // case buses + let bus = possible_bus_info.unwrap(); + if bus.signals.contains_key(signal){ + xtype = bus.signals.get(signal).unwrap().clone(); + xtype.reverse(); + possible_bus_info = None; + } else{ + let aux_info = bus.buses.get(signal).unwrap(); + xtype = aux_info.0.clone(); + xtype.reverse(); + possible_bus_info = Some(&aux_info.1); + } + } else{ + // case io component + let (signals, buses) = state.external_signals.get(name).unwrap(); + if signals.contains_key(signal){ + // case io signal + xtype = signals.get(signal).unwrap().clone(); + xtype.reverse(); + possible_bus_info = None; + } else{ + // case io bus + let aux_info = buses.get(signal).unwrap(); + xtype = aux_info.0.clone(); + xtype.reverse(); + possible_bus_info = Some(&aux_info.1); + } + } } } } xtype.reverse(); + if possible_bus_info.is_some(){ + xtype.push(possible_bus_info.unwrap().size); + } xtype } else { unreachable!(); diff --git a/compiler/src/hir/sugar_cleaner.rs b/compiler/src/hir/sugar_cleaner.rs index 693d58c6b..30561cff7 100644 --- a/compiler/src/hir/sugar_cleaner.rs +++ b/compiler/src/hir/sugar_cleaner.rs @@ -1,16 +1,19 @@ use super::very_concrete_program::*; use program_structure::ast::*; use num_traits::{ToPrimitive}; - +use std::collections::HashSet; struct ExtendedSyntax { initializations: Vec, } -struct Context {} +struct Context { + mixed_components: HashSet, +} struct State { fresh_id: usize, + inside_mixed_component: bool, } impl State { pub fn produce_id(&mut self) -> String { @@ -27,17 +30,24 @@ impl State { -Inline array removal -Initialization Block removal (no longer needed) -Uniform array removal + -Access to heterogeneus variables nested removal */ pub fn clean_sugar(vcp: &mut VCP) { - let mut state = State { fresh_id: 0 }; + let mut state = State { fresh_id: 0, inside_mixed_component: false}; for template in &mut vcp.templates { - let context = Context {}; + let mut mixed_components = HashSet::new(); + for cluster in &template.clusters{ + if let ClusterType::Mixed{..} = &cluster.xtype{ + mixed_components.insert(cluster.cmp_name.clone()); + } + } + let context = Context {mixed_components}; let trash = extend_statement(&mut template.code, &mut state, &context); assert!(trash.is_empty()); } for vcf in &mut vcp.functions { - let context = Context {}; + let context = Context {mixed_components: HashSet::new()}; let trash = extend_statement(&mut vcf.body, &mut state, &context); assert!(trash.is_empty()); } @@ -236,9 +246,13 @@ fn extend_number(_expr: &mut Expression, _state: &mut State, _context: &Context) fn extend_variable(expr: &mut Expression, state: &mut State, context: &Context) -> ExtendedSyntax { use Expression::Variable; - if let Variable { access, .. } = expr { + if let Variable { access, name, .. } = expr { let mut inits = vec![]; + let old_state_mixed = state.inside_mixed_component; // to recover at the end + let is_mixed_component = context.mixed_components.contains(name); + state.inside_mixed_component |= is_mixed_component; // to extend the indexes for acc in access { + if let Access::ArrayAccess(e) = acc { let mut expand = extend_expression(e, state, context); inits.append(&mut expand.initializations); @@ -247,6 +261,17 @@ fn extend_variable(expr: &mut Expression, state: &mut State, context: &Context) *e = expr.pop().unwrap(); } } + // recover all mixed_component state + state.inside_mixed_component = old_state_mixed; + + // in this case we move the value to initializations and return + // the new variable instead of the expression + // new_id = expr + if state.inside_mixed_component && is_mixed_component{ + let id = state.produce_id(); + *expr = rmv_sugar(&id, expr.clone(), &mut inits); + } + ExtendedSyntax { initializations: inits } } else { unreachable!(); diff --git a/compiler/src/hir/type_inference.rs b/compiler/src/hir/type_inference.rs index 02b82ee9d..ae70002bc 100644 --- a/compiler/src/hir/type_inference.rs +++ b/compiler/src/hir/type_inference.rs @@ -2,7 +2,7 @@ use super::analysis_utilities::*; use super::very_concrete_program::*; use program_structure::ast::*; use std::collections::HashSet; -use num_traits::{ToPrimitive}; +use num_traits::ToPrimitive; struct SearchInfo { environment: E, @@ -75,8 +75,16 @@ fn infer_type_block(stmt: &Statement, state: &State, context: &mut SearchInfo) - let mut returns = Option::None; let mut index = 0; context.environment.add_variable_block(); - while index < stmts.len() && returns.is_none() { - returns = infer_type_stmt(&stmts[index], state, context); + while index < stmts.len(){ + let new_value = infer_type_stmt(&stmts[index], state, context); + if new_value.is_some(){ + if returns.is_none(){ + returns = new_value; + } else{ + let max = std::cmp::max(new_value.unwrap(), returns.unwrap()); + returns = Some(max); + } + } index += 1; } context.environment.remove_variable_block(); @@ -109,14 +117,20 @@ fn infer_type_conditional( if let IfThenElse { if_case, else_case, .. } = stmt { let mut returns = infer_type_stmt(if_case, state, context); if let Option::Some(s) = else_case { - if returns.is_none() { - returns = infer_type_stmt(s, state, context); + let else_returns = infer_type_stmt(s, state, context); + if else_returns.is_some(){ + if returns.is_none(){ + returns = else_returns; + } else{ + let max = std::cmp::max(returns.unwrap(), else_returns.unwrap()); + returns = Some(max); + } } } returns } else { unreachable!(); - } + } } fn infer_type_while(stmt: &Statement, state: &State, context: &mut SearchInfo) -> Option { diff --git a/compiler/src/hir/very_concrete_program.rs b/compiler/src/hir/very_concrete_program.rs index f6834f9d7..811aea53f 100644 --- a/compiler/src/hir/very_concrete_program.rs +++ b/compiler/src/hir/very_concrete_program.rs @@ -24,6 +24,85 @@ impl PartialEq for Argument { } } +#[derive(Clone)] +pub enum Wire{ + TSignal(Signal), + TBus(Bus) +} +impl Wire { + + pub fn xtype(&self) -> SignalType { + match self{ + Wire::TSignal(s) => { + s.xtype + }, + Wire::TBus(s) => { + s.xtype + }, + } + } + pub fn name(&self) -> &String { + match self{ + Wire::TSignal(s) => { + &s.name + }, + Wire::TBus(s) => { + &s.name + }, + } + } + pub fn lengths(&self) -> &Vec { + match self{ + Wire::TSignal(s) => { + &s.lengths + }, + Wire::TBus(s) => { + &s.lengths + }, + } + } + pub fn local_id(&self) -> usize { + match self{ + Wire::TSignal(s) => { + s.local_id + }, + Wire::TBus(s) => { + s.local_id + }, + } + } + pub fn dag_local_id(&self) -> usize { + match self{ + Wire::TSignal(s) => { + s.dag_local_id + }, + Wire::TBus(s) => { + s.dag_local_id + }, + } + } + pub fn size(&self) -> usize { + match self{ + Wire::TSignal(s) => { + s.size + }, + Wire::TBus(s) => { + s.size + }, + } + } + pub fn bus_id(&self) -> Option { + match self{ + Wire::TSignal(_s) => { + None + }, + Wire::TBus(s) => { + Some(s.bus_id) + }, + } + } +} + #[derive(Clone)] pub struct Signal { pub name: String, @@ -31,12 +110,37 @@ pub struct Signal { pub xtype: SignalType, pub local_id: usize, pub dag_local_id: usize, + pub size: usize, } -impl Signal { - pub fn size(&self) -> usize { - self.lengths.iter().fold(1, |p, c| p * (*c)) - } + +#[derive(Clone)] +pub struct Bus{ + pub name: String, + pub lengths: Vec, + pub xtype: SignalType, + pub local_id: usize, + pub dag_local_id: usize, + pub bus_id: usize, // position of the bus in the table of the buses + pub size: usize, +} + +#[derive(Clone, Debug)] +pub struct FieldInfo{ + pub field_id: usize, + pub offset: usize, + pub dimensions: Vec, + pub size: usize, + pub bus_id: Option, // indicates the position of the bus in the table +} + + + +#[derive(Clone, Debug)] +pub struct BusInstance{ + pub name: String, + pub size: usize, + pub fields: BTreeMap, } #[derive(Clone)] @@ -59,7 +163,7 @@ pub struct Trigger { pub template_id: usize, pub component_name: String, pub indexed_with: Vec, - pub external_signals: Vec, + pub external_wires: Vec, pub has_inputs: bool, pub is_parallel: bool, } @@ -91,7 +195,7 @@ pub struct TemplateInstance { pub number_of_inputs: usize, pub number_of_outputs: usize, pub number_of_intermediates: usize, - pub signals: Vec, + pub wires: Vec, pub signals_to_tags: BTreeMap, pub components: Vec, pub number_of_components: usize, @@ -130,7 +234,7 @@ impl TemplateInstance { number_of_outputs: 0, number_of_intermediates: 0, number_of_components: config.number_of_components, - signals: Vec::new(), + wires: Vec::new(), components: config.components, triggers: config.triggers, clusters: config.clusters, @@ -138,10 +242,10 @@ impl TemplateInstance { } } - pub fn add_signal(&mut self, signal: Signal) { + pub fn add_signal(&mut self, wire: Wire) { use SignalType::*; - let new_signals = signal.lengths.iter().fold(1, |r, c| r * (*c)); - match signal.xtype { + let new_signals = wire.size(); + match wire.xtype() { Input => { self.number_of_inputs += new_signals; } @@ -152,7 +256,7 @@ impl TemplateInstance { self.number_of_intermediates += new_signals; } } - self.signals.push(signal); + self.wires.push(wire); } } @@ -188,6 +292,8 @@ pub struct VCPConfig { pub templates_in_mixed: Vec, pub program: ProgramArchive, pub prime: String, + pub buses: Vec, + } #[derive(Clone)] @@ -201,6 +307,8 @@ pub struct VCP { pub quick_knowledge: HashMap, pub templates_in_mixed: Vec, pub prime: String, + pub buses: Vec, + } impl VCP { pub fn new(config: VCPConfig) -> VCP { @@ -214,6 +322,7 @@ impl VCP { functions: vec![], quick_knowledge: HashMap::new(), prime: config.prime, + buses: config.buses }; super::merger::run_preprocessing(&mut vcp, config.program); vcp diff --git a/compiler/src/intermediate_representation/address_type.rs b/compiler/src/intermediate_representation/address_type.rs index ec05b3e90..cfdb3bc11 100644 --- a/compiler/src/intermediate_representation/address_type.rs +++ b/compiler/src/intermediate_representation/address_type.rs @@ -13,6 +13,22 @@ pub enum InputInformation { Input {status: StatusInput}, } +impl ToString for InputInformation { + fn to_string(&self) -> String { + use InputInformation::*; + match self { + NoInput => "NO_INPUT".to_string(), + Input { status } => { + match status { + StatusInput::Last => "LAST".to_string(), + StatusInput::NoLast => "NO_LAST".to_string(), + StatusInput::Unknown => "UNKNOWN".to_string(), + } + } + } + } +} + #[derive(Clone)] pub enum AddressType { Variable, @@ -26,7 +42,7 @@ impl ToString for AddressType { match self { Variable => "VARIABLE".to_string(), Signal => "SIGNAL".to_string(), - SubcmpSignal { cmp_address, .. } => format!("SUBCOMPONENT:{}", cmp_address.to_string()), + SubcmpSignal { cmp_address, input_information, .. } => format!("SUBCOMPONENT:{}:{}", cmp_address.to_string(), input_information.to_string()), } } } diff --git a/compiler/src/intermediate_representation/branch_bucket.rs b/compiler/src/intermediate_representation/branch_bucket.rs index 830b361ef..e0de88c19 100644 --- a/compiler/src/intermediate_representation/branch_bucket.rs +++ b/compiler/src/intermediate_representation/branch_bucket.rs @@ -46,7 +46,7 @@ impl ToString for BranchBucket { else_body = format!("{}{};", else_body, i.to_string()); } format!( - "IF(line:{},template_id:{},cond:{},if:{},else{})", + "IF(line:{},template_id:{},cond:{},if:{},else:{})", line, template_id, cond, if_body, else_body ) } diff --git a/compiler/src/intermediate_representation/call_bucket.rs b/compiler/src/intermediate_representation/call_bucket.rs index dbe6fb0ce..b96d7e878 100644 --- a/compiler/src/intermediate_representation/call_bucket.rs +++ b/compiler/src/intermediate_representation/call_bucket.rs @@ -92,16 +92,26 @@ impl WriteWasm for CallBucket { instructions.push(get_local(producer.get_call_lvar_tag())); instructions.push(set_constant(&count.to_string())); instructions.push(add32()); - if self.argument_types[i].size > 1 { + // TODO: We compute the possible sizes, case multiple size + // Now in case Multiple we just return the first value + // See below case C (complete) + let arg_size = match &self.argument_types[i].size{ + SizeOption::Single(value) => *value, + SizeOption::Multiple(_value) => { + assert!(false); //should never be the case! + 0 + } + }; + if arg_size > 1 { instructions.push(set_local(producer.get_store_aux_1_tag())); } let mut instructions_arg = p.produce_wasm(producer); instructions.append(&mut instructions_arg); - if self.argument_types[i].size == 1 { + if arg_size == 1 { instructions.push(call("$Fr_copy")); } else { instructions.push(set_local(producer.get_store_aux_2_tag())); - instructions.push(set_constant(&self.argument_types[i].size.to_string())); + instructions.push(set_constant(&arg_size.to_string())); instructions.push(set_local(producer.get_copy_counter_tag())); instructions.push(add_block()); instructions.push(add_loop()); @@ -131,7 +141,7 @@ impl WriteWasm for CallBucket { if producer.needs_comments() { instructions.push(format!(";; end copying argument {}", i)); } - count += self.argument_types[i].size * 4 * producer.get_size_32_bits_in_memory(); + count += arg_size * 4 * producer.get_size_32_bits_in_memory(); i += 1; } } @@ -184,8 +194,9 @@ impl WriteWasm for CallBucket { instructions.push(mul32()); instructions.push(add32()); instructions.push(load32(None)); //subcomponent block - instructions.push(set_local(producer.get_sub_cmp_tag())); - instructions.push(get_local(producer.get_sub_cmp_tag())); + instructions.push(tee_local(producer.get_sub_cmp_tag())); + //instructions.push(set_local(producer.get_sub_cmp_tag())); + //instructions.push(get_local(producer.get_sub_cmp_tag())); instructions.push(set_constant( &producer.get_signal_start_address_in_component().to_string(), )); @@ -212,8 +223,9 @@ impl WriteWasm for CallBucket { instructions.push(mul32()); instructions.push(add32()); instructions.push(load32(None)); //subcomponent block - instructions.push(set_local(producer.get_sub_cmp_tag())); - instructions.push(get_local(producer.get_sub_cmp_tag())); + instructions.push(tee_local(producer.get_sub_cmp_tag())); + //instructions.push(set_local(producer.get_sub_cmp_tag())); + //instructions.push(get_local(producer.get_sub_cmp_tag())); instructions.push(load32(None)); // get template id A instructions.push(set_constant("4")); //size in byte of i32 instructions.push(mul32()); @@ -224,41 +236,108 @@ impl WriteWasm for CallBucket { ))); // get position in component io signal to info list let signal_code_in_bytes = signal_code * 4; //position in the list of the signal code instructions.push(load32(Some(&signal_code_in_bytes.to_string()))); // get where the info of this signal is - //now we have first the offset and then the all size dimensions but the last one - if indexes.len() <= 1 { - instructions.push(load32(None)); // get signal offset (it is already the actual one in memory); - if indexes.len() == 1 { - let mut instructions_idx0 = - indexes[0].produce_wasm(producer); - instructions.append(&mut instructions_idx0); - let size = producer.get_size_32_bits_in_memory() * 4; - instructions.push(set_constant(&size.to_string())); - instructions.push(mul32()); - instructions.push(add32()); - } - } else { - instructions.push(set_local(producer.get_io_info_tag())); - instructions.push(get_local(producer.get_io_info_tag())); - instructions.push(load32(None)); // get signal offset (it is already the actual one in memory); - // compute de move with 2 or more dimensions - let mut instructions_idx0 = indexes[0].produce_wasm(producer); - instructions.append(&mut instructions_idx0); // start with dimension 0 - for i in 1..indexes.len() { - instructions.push(get_local(producer.get_io_info_tag())); - let offsetdim = 4 * i; - instructions.push(load32(Some(&offsetdim.to_string()))); // get size of ith dimension - instructions.push(mul32()); // multiply the current move by size of the ith dimension - let mut instructions_idxi = - indexes[i].produce_wasm(producer); - instructions.append(&mut instructions_idxi); - instructions.push(add32()); // add move upto dimension i - } - //we have the total move; and is multiplied by the size of memory Fr in bytes - let size = producer.get_size_32_bits_in_memory() * 4; - instructions.push(set_constant(&size.to_string())); - instructions.push(mul32()); // We have the total move in bytes - instructions.push(add32()); // add to the offset of the signal - } + //now we have first the offset, and then the all size dimensions but the last one + if indexes.len() == 0 { + instructions.push(load32(None)); // get signal offset (it is already the actual one in memory); + } else { + instructions.push(tee_local(producer.get_io_info_tag())); + instructions.push(load32(None)); // get offset; first slot in io_info (to start adding offsets) + // if the first access is qualified we place the address of the bus_id + if let AccessType::Qualified(_) = &indexes[0] { + instructions.push(get_local(producer.get_io_info_tag())); + instructions.push(load32(Some("4"))); // it is a bus, so the bus_id is in the second position + } + let mut idxpos = 0; + while idxpos < indexes.len() { + if let AccessType::Indexed(index_info) = &indexes[idxpos] { + let index_list = &index_info.indexes; + let dim = index_info.symbol_dim; + let mut infopos = 0; + assert!(index_list.len() > 0); + //We first compute the number of elements as + //((index_list[0] * length_of_dim[1]) + index_list[1]) * length_of_dim[2] + ... )* length_of_dim[n-1] + index_list[n-1] + //first position in the array access + let mut instructions_idx0 = index_list[0].produce_wasm(producer); + instructions.append(&mut instructions_idx0); + for i in 1..index_list.len() { + instructions.push(get_local(producer.get_io_info_tag())); + infopos += 4; //position in io or bus info of dimension of [1] (recall that first dimension is not added) + instructions.push(load32(Some(&infopos.to_string()))); // second dimension + instructions.push(mul32()); + let mut instructions_idxi = index_list[i].produce_wasm(producer); + instructions.append(&mut instructions_idxi); + instructions.push(add32()); + } + assert!(index_list.len() <= dim); + let diff = dim - index_list.len(); + if diff > 0 { + //println!("There is difference: {}",diff); + //instructions.push(format!(";; There is a difference {}", diff)); + // must be last access + assert!(idxpos+1 == indexes.len()); + instructions.push(get_local(producer.get_io_info_tag())); + infopos += 4; //position in io or bus info of the next dimension + instructions.push(load32(Some(&infopos.to_string()))); // length of next dimension + for _i in 1..diff { + //instructions.push(format!(";; Next dim {}", i)); + instructions.push(get_local(producer.get_io_info_tag())); + infopos += 4; //position in io or bus info of the next dimension + instructions.push(load32(Some(&infopos.to_string()))); // length of next dimension + instructions.push(mul32()); // multiply with previous dimensions + } + } // after this we have the product of the remaining dimensions + let field_size = producer.get_size_32_bits_in_memory() * 4; + instructions.push(set_constant(&field_size.to_string())); + instructions.push(get_local(producer.get_io_info_tag())); + infopos += 4; //position in io or bus info of size + instructions.push(load32(Some(&infopos.to_string()))); // size + instructions.push(mul32()); // size mult by size of field in bytes + if diff > 0 { + //instructions.push(format!(";; Multiply dimensions")); + instructions.push(mul32()); // total size of the content according to the missing dimensions + } + instructions.push(mul32()); // total offset in the array + instructions.push(add32()); // to the current offset + idxpos += 1; + if idxpos < indexes.len() { + //next must be Qualified + if let AccessType::Indexed(_) = &indexes[idxpos] { + assert!(false); + } + // we add the type of bus it is + instructions.push(get_local(producer.get_io_info_tag())); + infopos += 4; + instructions.push(load32(Some(&infopos.to_string()))); // bus_id + } + } else if let AccessType::Qualified(field_no) = &indexes[idxpos] { + //we have on the stack the bus_id + instructions.push(set_constant("4")); //size in byte of i32 + instructions.push(mul32()); //maybe better in the memory like this + instructions.push(load32(Some( + &producer.get_bus_instance_to_field_start().to_string() + ))); // get position in the bus to field in memory + let field_no_bytes = field_no * 4; + instructions.push(load32(Some(&field_no_bytes.to_string()))); // get position in the field info in memory + if idxpos +1 < indexes.len() { + instructions.push(tee_local(producer.get_io_info_tag())); + } + //let field_size = producer.get_size_32_bits_in_memory() * 4; + //instructions.push(set_constant(&field_size.to_string())); + instructions.push(load32(None)); // get the offset + //instructions.push(mul32()); // mult by size of field in bytes + instructions.push(add32()); // add to the current offset + idxpos += 1; + if idxpos < indexes.len() { + if let AccessType::Qualified(_) = &indexes[idxpos] { + instructions.push(get_local(producer.get_io_info_tag())); + instructions.push(load32(Some("4"))); // bus_id + } + } + } else { + assert!(false); + } + } + } instructions.push(get_local(producer.get_sub_cmp_tag())); instructions.push(set_constant( &producer.get_signal_start_address_in_component().to_string(), @@ -273,7 +352,20 @@ impl WriteWasm for CallBucket { } } } - instructions.push(set_constant(&data.context.size.to_string())); + // We check if we have to compute the possible sizes, case multiple size + match &data.context.size{ + SizeOption::Single(value) => { + instructions.push(set_constant(&value.to_string())); + } + SizeOption::Multiple(values) => { //create a nested if-else with all cases + instructions.push(get_local(producer.get_sub_cmp_tag())); + instructions.push(load32(None)); // get template id + instructions.push(set_local(producer.get_aux_0_tag())); + let mut instr_if = create_if_selection(&values,producer.get_aux_0_tag()); + instructions.append(&mut instr_if); + instructions.push(tee_local(producer.get_result_size_tag())); + } + }; instructions.push(call(&format!("${}", self.symbol))); instructions.push(tee_local(producer.get_merror_tag())); instructions.push(add_if()); @@ -295,7 +387,14 @@ impl WriteWasm for CallBucket { instructions.push(load32(Some( &producer.get_input_counter_address_in_component().to_string(), ))); //remaining inputs to be set - instructions.push(set_constant(&data.context.size.to_string())); + match &data.context.size{ + SizeOption::Single(value) => { + instructions.push(set_constant(&value.to_string())); + } + SizeOption::Multiple(_) => { + instructions.push(get_local(producer.get_result_size_tag())); + } + }; instructions.push(sub32()); instructions.push(store32(Some( &producer.get_input_counter_address_in_component().to_string(), @@ -381,9 +480,17 @@ impl WriteC for CallBucket { let (mut prologue_value, src) = p.produce_c(producer, parallel); prologue.append(&mut prologue_value); let arena_position = format!("&{}[{}]", L_VAR_FUNC_CALL_STORAGE, count); - if self.argument_types[i].size > 1 { + // TODO, CASE CALL ARGUMENTS + let size = match &self.argument_types[i].size{ + SizeOption::Single(value) => *value, + SizeOption::Multiple(_values) => { + assert!(false); //should never be the case! + 0 + } + }; + if size > 1 { let copy_arguments = - vec![arena_position, src, self.argument_types[i].size.to_string()]; + vec![arena_position, src, size.to_string()]; prologue .push(format!("{};", build_call("Fr_copyn".to_string(), copy_arguments))); } else { @@ -392,7 +499,7 @@ impl WriteC for CallBucket { .push(format!("{};", build_call("Fr_copy".to_string(), copy_arguments))); } prologue.push(format!("// end copying argument {}", i)); - count += self.argument_types[i].size; + count += size; i += 1; } let result; @@ -415,37 +522,89 @@ impl WriteC for CallBucket { let (mut cmp_prologue, cmp_index) = cmp_address.produce_c(producer, parallel); prologue.append(&mut cmp_prologue); prologue.push(format!("{{")); - prologue.push(format!("uint {} = {};", cmp_index_ref, cmp_index)); - + prologue.push(format!("uint {} = {};", cmp_index_ref, cmp_index)); } + let size = match &data.context.size{ + SizeOption::Single(value) => value.to_string(), + SizeOption::Multiple(values) => { + prologue.push(format!("std::map size_store {};", + set_list_tuple(values.to_vec()) + )); + let sub_component_pos_in_memory = format!("{}[{}]",MY_SUBCOMPONENTS,cmp_index_ref); + let temp_id = template_id_in_component(sub_component_pos_in_memory); + format!("size_store[{}]", temp_id) + } + }; let ((mut dest_prologue, dest_index), my_template_header) = if let LocationRule::Indexed { location, template_header } = &data.dest { (location.produce_c(producer, parallel), template_header.clone()) - } else if let LocationRule::Mapped { signal_code, indexes } = &data.dest { - let mut map_prologue = vec![]; + } else if let LocationRule::Mapped { signal_code, indexes} = &data.dest { + let mut map_prologue = vec![]; let sub_component_pos_in_memory = format!("{}[{}]",MY_SUBCOMPONENTS,cmp_index_ref.clone()); let mut map_access = format!("{}->{}[{}].defs[{}].offset", circom_calc_wit(), template_ins_2_io_info(), template_id_in_component(sub_component_pos_in_memory.clone()), signal_code.to_string()); - if indexes.len()>0 { + if indexes.len() > 0 { + map_prologue.push(format!("{{")); + map_prologue.push(format!("uint map_accesses_aux[{}];",indexes.len().to_string())); map_prologue.push(format!("{{")); - map_prologue.push(format!("uint map_index_aux[{}];",indexes.len().to_string())); - let (mut index_code_0, mut map_index) = indexes[0].produce_c(producer, parallel); - map_prologue.append(&mut index_code_0); - map_prologue.push(format!("map_index_aux[0]={};",map_index)); - map_index = format!("map_index_aux[0]"); - for i in 1..indexes.len() { - let (mut index_code, index_exp) = indexes[i].produce_c(producer, parallel); - map_prologue.append(&mut index_code); - map_prologue.push(format!("map_index_aux[{}]={};",i.to_string(),index_exp)); - map_index = format!("({})*{}->{}[{}].defs[{}].lengths[{}]+map_index_aux[{}]", - map_index, circom_calc_wit(), template_ins_2_io_info(), - template_id_in_component(sub_component_pos_in_memory.clone()), - signal_code.to_string(),(i-1).to_string(),i.to_string()); + //cur_def contains a pointer to the definion of the next acces. + //The first time it is taken from template_ins_2_io_info + map_prologue.push(format!("IOFieldDef *cur_def = &({}->{}[{}].defs[{}]);", + circom_calc_wit(), template_ins_2_io_info(), + template_id_in_component(sub_component_pos_in_memory.clone()), + signal_code.to_string())); + let mut idxpos = 0; + while idxpos < indexes.len() { + if let AccessType::Indexed(index_info) = &indexes[idxpos] { + let index_list = &index_info.indexes; + let dim = index_info.symbol_dim; + map_prologue.push(format!("{{")); + map_prologue.push(format!("uint map_index_aux[{}];",index_list.len().to_string())); + //We first compute the number of elements as + //((map_index_aux[0] * length_of_dim[1]) + map_index_aux[1]) * length_of_dim[2] + ... )* length_of_dim[n-1] + map_index_aux[n-1] with + // map_index_aux[i] = computation of index_list[i] + let (mut index_code_0, mut map_index) = index_list[0].produce_c(producer, parallel); + map_prologue.append(&mut index_code_0); + map_prologue.push(format!("map_index_aux[0]={};",map_index)); + map_index = format!("map_index_aux[0]"); + for i in 1..index_list.len() { + let (mut index_code, index_exp) = index_list[i].produce_c(producer, parallel); + map_prologue.append(&mut index_code); + map_prologue.push(format!("map_index_aux[{}]={};",i.to_string(),index_exp)); + // multiply the offset inthe array by the size of the elements + map_index = format!("({})*cur_def->lengths[{}]+map_index_aux[{}]", + map_index,(i-1).to_string(),i.to_string()); + } + assert!(index_list.len() <= dim); + if dim - index_list.len() > 0 { + map_prologue.push(format!("//There is a difference {};",dim - index_list.len())); + // must be last access + assert!(idxpos+1 == indexes.len()); + for i in index_list.len()..dim { + map_index = format!("{}*cur_def->lengths[{}]", + map_index, (i-1).to_string()); + } // after this we have multiplied by the remaining dimensions + } + // multiply the offset in the array (after multiplying by the missing dimensions) by the size of the elements + map_prologue.push(format!("map_accesses_aux[{}] = {}*cur_def->size;", idxpos.to_string(), map_index)); + map_prologue.push(format!("}}")); + } else if let AccessType::Qualified(field_no) = &indexes[idxpos] { + map_prologue.push(format!("cur_def = &({}->{}[cur_def->busId].defs[{}]);", + circom_calc_wit(), bus_ins_2_field_info(), + field_no.to_string())); + map_prologue.push(format!("map_accesses_aux[{}] = cur_def->offset;", idxpos.to_string())); + } else { + assert!(false); + } + // add to the access expression the computed offset + map_access = format!("{}+map_accesses_aux[{}]", + map_access, idxpos.to_string()); + idxpos += 1; } - map_access = format!("{}+{}",map_access,map_index); + map_prologue.push(format!("}}")); } ((map_prologue, map_access),Some(template_id_in_component(sub_component_pos_in_memory.clone()))) } else { @@ -472,7 +631,7 @@ impl WriteC for CallBucket { } }; call_arguments.push(result_ref); - call_arguments.push(data.context.size.to_string()); + call_arguments.push(size.clone()); prologue.push(format!("{};", build_call(self.symbol.clone(), call_arguments))); if let LocationRule::Mapped { indexes, .. } = &data.dest { if indexes.len() > 0 { @@ -482,9 +641,9 @@ impl WriteC for CallBucket { // if output and parallel send notify if let AddressType::Signal = &data.dest_address_type { if parallel.unwrap() && data.dest_is_output { - if data.context.size > 0 { + if size != "0" { prologue.push(format!("{{")); - prologue.push(format!("for (int i = 0; i < {}; i++) {{",data.context.size)); + prologue.push(format!("for (int i = 0; i < {}; i++) {{", size)); prologue.push(format!("{}->componentMemory[{}].mutexes[{}+i].lock();",CIRCOM_CALC_WIT,CTX_INDEX,dest_index.clone())); prologue.push(format!("{}->componentMemory[{}].outputIsSet[{}+i]=true;",CIRCOM_CALC_WIT,CTX_INDEX,dest_index.clone())); prologue.push(format!("{}->componentMemory[{}].mutexes[{}+i].unlock();",CIRCOM_CALC_WIT,CTX_INDEX,dest_index.clone())); @@ -509,7 +668,7 @@ impl WriteC for CallBucket { ); let sub_cmp_counter_decrease = format!( "{} -= {}", - sub_cmp_counter, &data.context.size + sub_cmp_counter, size ); if let InputInformation::Input{status} = input_information { if let StatusInput::NoLast = status { diff --git a/compiler/src/intermediate_representation/compute_bucket.rs b/compiler/src/intermediate_representation/compute_bucket.rs index 1f14a1ffa..31a4498cb 100644 --- a/compiler/src/intermediate_representation/compute_bucket.rs +++ b/compiler/src/intermediate_representation/compute_bucket.rs @@ -3,7 +3,7 @@ use crate::translating_traits::*; use code_producers::c_elements::*; use code_producers::wasm_elements::*; -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] pub enum OperatorType { Mul, Div, diff --git a/compiler/src/intermediate_representation/ir_interface.rs b/compiler/src/intermediate_representation/ir_interface.rs index 5dc9fc33d..88e5e6497 100644 --- a/compiler/src/intermediate_representation/ir_interface.rs +++ b/compiler/src/intermediate_representation/ir_interface.rs @@ -6,12 +6,14 @@ pub use super::compute_bucket::{ComputeBucket, OperatorType}; pub use super::create_component_bucket::CreateCmpBucket; pub use super::load_bucket::LoadBucket; pub use super::location_rule::LocationRule; +pub use super::location_rule::AccessType; +pub use super::location_rule::IndexedInfo; pub use super::log_bucket::LogBucket; pub use super::loop_bucket::LoopBucket; pub use super::return_bucket::ReturnBucket; pub use super::store_bucket::StoreBucket; pub use super::log_bucket::LogBucketArg; -pub use super::types::{InstrContext, ValueType}; +pub use super::types::{InstrContext, ValueType, SizeOption}; pub use super::value_bucket::ValueBucket; use crate::translating_traits::*; @@ -31,7 +33,7 @@ pub trait ObtainMeta { } pub trait CheckCompute { - fn has_compute_in(&self) -> bool; + fn _has_compute_in(&self) -> bool; } pub type InstructionList = Vec; @@ -95,7 +97,7 @@ impl ObtainMeta for Instruction { } impl CheckCompute for Instruction { - fn has_compute_in(&self) -> bool { + fn _has_compute_in(&self) -> bool { use Instruction::*; match self { Load(_v) => {true diff --git a/compiler/src/intermediate_representation/load_bucket.rs b/compiler/src/intermediate_representation/load_bucket.rs index 18001e331..9e4b523da 100644 --- a/compiler/src/intermediate_representation/load_bucket.rs +++ b/compiler/src/intermediate_representation/load_bucket.rs @@ -93,11 +93,11 @@ impl WriteWasm for LoadBucket { instructions.push(";; end of load bucket".to_string()); } } - LocationRule::Mapped { signal_code, indexes } => { + LocationRule::Mapped { signal_code, indexes} => { match &self.address_type { AddressType::SubcmpSignal { cmp_address, .. } => { if producer.needs_comments() { - instructions.push(";; is subcomponent".to_string()); + instructions.push(";; is subcomponent mapped".to_string()); } instructions.push(get_local(producer.get_offset_tag())); instructions.push(set_constant( @@ -110,8 +110,9 @@ impl WriteWasm for LoadBucket { instructions.push(mul32()); instructions.push(add32()); instructions.push(load32(None)); //subcomponent block - instructions.push(set_local(producer.get_sub_cmp_load_tag())); - instructions.push(get_local(producer.get_sub_cmp_load_tag())); + instructions.push(tee_local(producer.get_sub_cmp_load_tag())); + //instructions.push(set_local(producer.get_sub_cmp_load_tag())); + //instructions.push(get_local(producer.get_sub_cmp_load_tag())); instructions.push(load32(None)); // get template id A instructions.push(set_constant("4")); //size in byte of i32 instructions.push(mul32()); @@ -120,39 +121,112 @@ impl WriteWasm for LoadBucket { ))); // get position in component io signal to info list let signal_code_in_bytes = signal_code * 4; //position in the list of the signal code instructions.push(load32(Some(&signal_code_in_bytes.to_string()))); // get where the info of this signal is - //now we have first the offset and then the all size dimensions but the last one - if indexes.len() <= 1 { - instructions.push(load32(None)); // get signal offset (it is already the actual one in memory); - if indexes.len() == 1 { - let mut instructions_idx0 = indexes[0].produce_wasm(producer); - instructions.append(&mut instructions_idx0); - let size = producer.get_size_32_bits_in_memory() * 4; - instructions.push(set_constant(&size.to_string())); - instructions.push(mul32()); - instructions.push(add32()); - } - } else { - instructions.push(set_local(producer.get_io_info_tag())); - instructions.push(get_local(producer.get_io_info_tag())); - instructions.push(load32(None)); // get signal offset (it is already the actual one in memory); - // compute de move with 2 or more dimensions - let mut instructions_idx0 = indexes[0].produce_wasm(producer); - instructions.append(&mut instructions_idx0); // start with dimension 0 - for i in 1..indexes.len() { - instructions.push(get_local(producer.get_io_info_tag())); - let offsetdim = 4 * i; - instructions.push(load32(Some(&offsetdim.to_string()))); // get size of ith dimension - instructions.push(mul32()); // multiply the current move by size of the ith dimension - let mut instructions_idxi = indexes[i].produce_wasm(producer); - instructions.append(&mut instructions_idxi); - instructions.push(add32()); // add move upto dimension i - } - //we have the total move; and is multiplied by the size of memory Fr in bytes - let size = producer.get_size_32_bits_in_memory() * 4; - instructions.push(set_constant(&size.to_string())); - instructions.push(mul32()); // We have the total move in bytes - instructions.push(add32()); // add to the offset of the signal - } + //now we have first the offset, and then the all size dimensions but the last one + if indexes.len() == 0 { + //instructions.push(";; has no indexes".to_string()); + instructions.push(load32(None)); // get signal offset (it is already the actual one in memory); + } else { + //instructions.push(";; has indexes".to_string()); + instructions.push(tee_local(producer.get_io_info_tag())); + instructions.push(load32(None)); // get offset; first slot in io_info (to start adding offsets) + // if the first access is qualified we place the address of the bus_id + if let AccessType::Qualified(_) = &indexes[0] { + instructions.push(get_local(producer.get_io_info_tag())); + instructions.push(load32(Some("4"))); // it is a bus, so the bus_id is in the second position + } + let mut idxpos = 0; + while idxpos < indexes.len() { + if let AccessType::Indexed(index_info) = &indexes[idxpos] { + + let index_list = &index_info.indexes; + let dim = index_info.symbol_dim; + + let mut infopos = 0; + assert!(index_list.len() > 0); + //We first compute the number of elements as + //((index_list[0] * length_of_dim[1]) + index_list[1]) * length_of_dim[2] + ... )* length_of_dim[n-1] + index_list[n-1] + //first position in the array access + let mut instructions_idx0 = index_list[0].produce_wasm(producer); + instructions.append(&mut instructions_idx0); + for i in 1..index_list.len() { + instructions.push(get_local(producer.get_io_info_tag())); + infopos += 4; //position in io or bus info of dimension of [1] (recall that first dimension is not added) + instructions.push(load32(Some(&infopos.to_string()))); // second dimension + instructions.push(mul32()); + let mut instructions_idxi = index_list[i].produce_wasm(producer); + instructions.append(&mut instructions_idxi); + instructions.push(add32()); + } + assert!(index_list.len() <= dim); + let diff = dim - index_list.len(); + if diff > 0 { + //println!("There is difference: {}",diff); + //instructions.push(format!(";; There is a difference {}", diff)); + // must be last access + assert!(idxpos+1 == indexes.len()); + instructions.push(get_local(producer.get_io_info_tag())); + infopos += 4; //position in io or bus info of the next dimension + instructions.push(load32(Some(&infopos.to_string()))); // length of next dimension + for _i in 1..diff { + //instructions.push(format!(";; Next dim {}", i)); + instructions.push(get_local(producer.get_io_info_tag())); + infopos += 4; //position in io or bus info of the next dimension + instructions.push(load32(Some(&infopos.to_string()))); // length of next dimension + instructions.push(mul32()); // multiply with previous dimensions + } + } // after this we have the product of the remaining dimensions + let field_size = producer.get_size_32_bits_in_memory() * 4; + instructions.push(set_constant(&field_size.to_string())); + instructions.push(get_local(producer.get_io_info_tag())); + infopos += 4; //position in io or bus info of size + instructions.push(load32(Some(&infopos.to_string()))); // size + instructions.push(mul32()); // size mult by size of field in bytes + if diff > 0 { + //instructions.push(format!(";; Multiply dimensions")); + instructions.push(mul32()); // total size of the content according to the missing dimensions + } + instructions.push(mul32()); // total offset in the array + instructions.push(add32()); // to the current offset + idxpos += 1; + if idxpos < indexes.len() { + //next must be Qualified + if let AccessType::Indexed(_) = &indexes[idxpos] { + assert!(false); + } + // we add the type of bus it is + instructions.push(get_local(producer.get_io_info_tag())); + infopos += 4; + instructions.push(load32(Some(&infopos.to_string()))); // bus_id + } + } else if let AccessType::Qualified(field_no) = &indexes[idxpos] { + //we have on the stack the bus_id + instructions.push(set_constant("4")); //size in byte of i32 + instructions.push(mul32()); //maybe better in the memory like this + instructions.push(load32(Some( + &producer.get_bus_instance_to_field_start().to_string() + ))); // get position in the bus to field in memory + let field_no_bytes = field_no * 4; + instructions.push(load32(Some(&field_no_bytes.to_string()))); // get position in the field info in memory + if idxpos +1 < indexes.len() { + instructions.push(tee_local(producer.get_io_info_tag())); + } + //let field_size = producer.get_size_32_bits_in_memory() * 4; + //instructions.push(set_constant(&field_size.to_string())); + instructions.push(load32(None)); // get the offset + //instructions.push(mul32()); // mult by size of field in bytes + instructions.push(add32()); // add to the current offset + idxpos += 1; + if idxpos < indexes.len() { + if let AccessType::Qualified(_) = &indexes[idxpos] { + instructions.push(get_local(producer.get_io_info_tag())); + instructions.push(load32(Some("4"))); // bus_id + } + } + } else { + assert!(false); + } + } + } instructions.push(get_local(producer.get_sub_cmp_load_tag())); instructions.push(set_constant( &producer.get_signal_start_address_in_component().to_string(), @@ -192,30 +266,65 @@ impl WriteC for LoadBucket { if let LocationRule::Indexed { location, .. } = &self.src { location.produce_c(producer, parallel) } else if let LocationRule::Mapped { signal_code, indexes } = &self.src { - let mut map_prologue = vec![]; + let mut map_prologue = vec![]; let sub_component_pos_in_memory = format!("{}[{}]",MY_SUBCOMPONENTS,cmp_index_ref.clone()); let mut map_access = format!("{}->{}[{}].defs[{}].offset", circom_calc_wit(), template_ins_2_io_info(), template_id_in_component(sub_component_pos_in_memory.clone()), signal_code.to_string()); - if indexes.len()>0 { - let (mut index_code_0, mut map_index) = indexes[0].produce_c(producer, parallel); - map_prologue.append(&mut index_code_0); - for i in 1..indexes.len() { - let (mut index_code, index_exp) = indexes[i].produce_c(producer, parallel); - map_prologue.append(&mut index_code); - map_index = format!("({})*{}->{}[{}].defs[{}].lengths[{}]+{}", - map_index, circom_calc_wit(), template_ins_2_io_info(), + if indexes.len() > 0 { + //cur_def contains a string that goes to the definion of the next acces. + //The first time it is taken from template_ins_2_io_info + let mut cur_def = format!("{}->{}[{}].defs[{}]", + circom_calc_wit(), template_ins_2_io_info(), template_id_in_component(sub_component_pos_in_memory.clone()), - signal_code.to_string(), (i-1).to_string(),index_exp); - } - map_access = format!("{}+{}",map_access,map_index); + signal_code.to_string()); + let mut idxpos = 0; + while idxpos < indexes.len() { + if let AccessType::Indexed(index_info) = &indexes[idxpos] { + let index_list = &index_info.indexes; + let dim = index_info.symbol_dim; + //We first compute the number of elements as + //((index_list[0] * length_of_dim[1]) + index_list[1]) * length_of_dim[2] + ... )* length_of_dim[n-1] + index_list[n-1] + let (mut index_code_0, mut map_index) = index_list[0].produce_c(producer, parallel); + map_prologue.append(&mut index_code_0); + for i in 1..index_list.len() { + let (mut index_code, index_exp) = index_list[i].produce_c(producer, parallel); + map_prologue.append(&mut index_code); + map_index = format!("({})*({}.lengths[{}])+{}", + map_index,cur_def,(i-1).to_string(),index_exp); + } + assert!(index_list.len() <= dim); + if dim - index_list.len() > 0 { + map_prologue.push(format!("//There is a difference {};",dim - index_list.len())); + // must be last access + assert!(idxpos+1 == indexes.len()); + for i in index_list.len()..dim { + map_index = format!("{}*{}.lengths[{}]", + map_index, cur_def, (i-1).to_string()); + } // after this we have multiplied by the remaining dimensions + } + // multiply the offset in the array (after multiplying by the missing dimensions) by the size of the elements + // and add it to the access expression + map_access = format!("{}+({})*{}.size", map_access, map_index, cur_def); + } else if let AccessType::Qualified(field_no) = &indexes[idxpos] { + cur_def = format!("{}->{}[{}.busId].defs[{}]", + circom_calc_wit(), bus_ins_2_field_info(), + cur_def, field_no.to_string()); + map_access = format!("{}+{}.offset", map_access, cur_def); + } else { + assert!(false); + } + idxpos += 1; + } } (map_prologue, map_access) } else { assert!(false); (vec![], "".to_string()) }; + + prologue.append(&mut src_prologue); let access = match &self.address_type { AddressType::Variable => { @@ -226,13 +335,26 @@ impl WriteC for LoadBucket { } AddressType::SubcmpSignal { uniform_parallel_value, is_output, .. } => { if *is_output { + // We compute the possible sizes, case multiple size + let size = match &self.context.size{ + SizeOption::Single(value) => value.to_string(), + SizeOption::Multiple(values) => { + prologue.push(format!("std::map size_store {};", + set_list_tuple(values.clone()) + )); + let sub_component_pos_in_memory = format!("{}[{}]",MY_SUBCOMPONENTS,cmp_index_ref); + let temp_id = template_id_in_component(sub_component_pos_in_memory); + format!("size_load[{}]", temp_id) + } + }; if uniform_parallel_value.is_some(){ if uniform_parallel_value.unwrap(){ prologue.push(format!("{{")); prologue.push(format!("int aux1 = {};",cmp_index_ref.clone())); prologue.push(format!("int aux2 = {};",src_index.clone())); // check each one of the outputs of the assignment, we add i to check them one by one - prologue.push(format!("for (int i = 0; i < {}; i++) {{",self.context.size)); + + prologue.push(format!("for (int i = 0; i < {}; i++) {{", size)); prologue.push(format!("ctx->numThreadMutex.lock();")); prologue.push(format!("ctx->numThread--;")); //prologue.push(format!("printf(\"%i \\n\", ctx->numThread);")); @@ -268,7 +390,7 @@ impl WriteC for LoadBucket { prologue.push(format!("int aux1 = {};",cmp_index_ref.clone())); prologue.push(format!("int aux2 = {};",src_index.clone())); // check each one of the outputs of the assignment, we add i to check them one by one - prologue.push(format!("for (int i = 0; i < {}; i++) {{",self.context.size)); + prologue.push(format!("for (int i = 0; i < {}; i++) {{", size)); prologue.push(format!("ctx->numThreadMutex.lock();")); prologue.push(format!("ctx->numThread--;")); //prologue.push(format!("printf(\"%i \\n\", ctx->numThread);")); diff --git a/compiler/src/intermediate_representation/location_rule.rs b/compiler/src/intermediate_representation/location_rule.rs index e28e19c1d..47fdefc80 100644 --- a/compiler/src/intermediate_representation/location_rule.rs +++ b/compiler/src/intermediate_representation/location_rule.rs @@ -1,9 +1,38 @@ use super::ir_interface::*; +#[derive(Clone)] +pub struct IndexedInfo{ + pub indexes: Vec, + pub symbol_dim: usize +} + +#[derive(Clone)] +pub enum AccessType{ + Indexed(IndexedInfo), // Case accessing an array + Qualified(usize), // Case accessing a field -> id field +} + +impl ToString for AccessType { + fn to_string(&self) -> String { + match &self{ + AccessType::Indexed(index) =>{ + + format!("Indexed({},{})", index.symbol_dim, index.indexes.iter().map(|i| i.to_string()).collect::()) + } + AccessType::Qualified(value) =>{ + format!("field({})", value) + } + } + } +} + +// Example: accessing a[2][3].b[2].c +// [Indexed([2, 3]), Qualified(id_b), Indexed([2]), Qualified(id_c)] + #[derive(Clone)] pub enum LocationRule { Indexed { location: InstructionPointer, template_header: Option }, - Mapped { signal_code: usize, indexes: Vec }, + Mapped { signal_code: usize, indexes: Vec }, } impl ToString for LocationRule { diff --git a/compiler/src/intermediate_representation/return_bucket.rs b/compiler/src/intermediate_representation/return_bucket.rs index b4b541e41..6ecec4570 100644 --- a/compiler/src/intermediate_representation/return_bucket.rs +++ b/compiler/src/intermediate_representation/return_bucket.rs @@ -100,9 +100,12 @@ impl WriteC for ReturnBucket { instructions.push("// return bucket".to_string()); let (mut instructions_value, src) = self.value.produce_c(producer, parallel); instructions.append(&mut instructions_value); + if self.with_size > 1 { + let final_size = format!("std::min({},{})", self.with_size, FUNCTION_DESTINATION_SIZE); + let copy_arguments = - vec![FUNCTION_DESTINATION.to_string(), src, FUNCTION_DESTINATION_SIZE.to_string()]; + vec![FUNCTION_DESTINATION.to_string(), src, final_size]; instructions.push(format!("{};", build_call("Fr_copyn".to_string(), copy_arguments))); } else { let copy_arguments = vec![FUNCTION_DESTINATION.to_string(), src]; diff --git a/compiler/src/intermediate_representation/store_bucket.rs b/compiler/src/intermediate_representation/store_bucket.rs index 25111ddf1..0d8ea66a4 100644 --- a/compiler/src/intermediate_representation/store_bucket.rs +++ b/compiler/src/intermediate_representation/store_bucket.rs @@ -8,8 +8,10 @@ pub struct StoreBucket { pub line: usize, pub message_id: usize, pub context: InstrContext, + pub src_context: InstrContext, pub dest_is_output: bool, pub dest_address_type: AddressType, + pub src_address_type: Option, pub dest: LocationRule, pub src: InstructionPointer, } @@ -53,7 +55,26 @@ impl WriteWasm for StoreBucket { fn produce_wasm(&self, producer: &WASMProducer) -> Vec { use code_producers::wasm_elements::wasm_code_generator::*; let mut instructions = vec![]; - if self.context.size == 0 { + + // We check if we have to compute the possible sizes, case multiple size + let mut is_multiple_dest = false; + let (size_dest,values_dest) = match &self.context.size{ + SizeOption::Single(value) => (*value,vec![]), + SizeOption::Multiple(values) => { + is_multiple_dest = true; + (values.len(),values.clone()) + } + }; + let mut is_multiple_src = false; + let (size_src,values_src) = match &self.src_context.size{ + SizeOption::Single(value) => (*value,vec![]), + SizeOption::Multiple(values) => { + is_multiple_src = true; + (values.len(),values.clone()) + } + }; + + if size_dest == 0 || size_src == 0 { return vec![]; } if producer.needs_comments() { @@ -90,8 +111,9 @@ impl WriteWasm for StoreBucket { instructions.push(mul32()); instructions.push(add32()); instructions.push(load32(None)); //subcomponent block - instructions.push(set_local(producer.get_sub_cmp_tag())); - instructions.push(get_local(producer.get_sub_cmp_tag())); + instructions.push(tee_local(producer.get_sub_cmp_tag())); + //instructions.push(set_local(producer.get_sub_cmp_tag())); + //instructions.push(get_local(producer.get_sub_cmp_tag())); instructions.push(set_constant( &producer.get_signal_start_address_in_component().to_string(), )); @@ -105,7 +127,7 @@ impl WriteWasm for StoreBucket { match &self.dest_address_type { AddressType::SubcmpSignal { cmp_address, .. } => { if producer.needs_comments() { - instructions.push(";; is subcomponent".to_string()); + instructions.push(";; is subcomponent mapped".to_string()); } instructions.push(get_local(producer.get_offset_tag())); instructions.push(set_constant( @@ -118,9 +140,10 @@ impl WriteWasm for StoreBucket { instructions.push(mul32()); instructions.push(add32()); instructions.push(load32(None)); //subcomponent block - instructions.push(set_local(producer.get_sub_cmp_tag())); - instructions.push(get_local(producer.get_sub_cmp_tag())); - instructions.push(load32(None)); // get template id A + instructions.push(tee_local(producer.get_sub_cmp_tag())); + //instructions.push(set_local(producer.get_sub_cmp_tag())); + //instructions.push(get_local(producer.get_sub_cmp_tag())); + instructions.push(load32(None)); // get template id instructions.push(set_constant("4")); //size in byte of i32 instructions.push(mul32()); instructions.push(load32(Some( @@ -129,44 +152,115 @@ impl WriteWasm for StoreBucket { let signal_code_in_bytes = signal_code * 4; //position in the list of the signal code instructions.push(load32(Some(&signal_code_in_bytes.to_string()))); // get where the info of this signal is //now we have first the offset, and then the all size dimensions but the last one - if indexes.len() <= 1 { - instructions.push(load32(None)); // get signal offset (it is already the actual one in memory); - if indexes.len() == 1 { - let mut instructions_idx0 = indexes[0].produce_wasm(producer); - instructions.append(&mut instructions_idx0); - let size = producer.get_size_32_bits_in_memory() * 4; - instructions.push(set_constant(&size.to_string())); - instructions.push(mul32()); - instructions.push(add32()); - } - } else { - instructions.push(set_local(producer.get_io_info_tag())); - instructions.push(get_local(producer.get_io_info_tag())); - instructions.push(load32(None)); // get signal offset (it is already the actual one in memory); - // compute de move with 2 or more dimensions - let mut instructions_idx0 = indexes[0].produce_wasm(producer); - instructions.append(&mut instructions_idx0); // start with dimension 0 - for i in 1..indexes.len() { - instructions.push(get_local(producer.get_io_info_tag())); - let offsetdim = 4 * i; - instructions.push(load32(Some(&offsetdim.to_string()))); // get size of ith dimension - instructions.push(mul32()); // multiply the current move by size of the ith dimension - let mut instructions_idxi = indexes[i].produce_wasm(producer); - instructions.append(&mut instructions_idxi); - instructions.push(add32()); // add move upto dimension i - } - //we have the total move; and is multiplied by the size of memory Fr in bytes - let size = producer.get_size_32_bits_in_memory() * 4; - instructions.push(set_constant(&size.to_string())); - instructions.push(mul32()); // We have the total move in bytes - instructions.push(add32()); // add to the offset of the signal - } + if indexes.len() == 0 { + instructions.push(";; has no indexes".to_string()); + instructions.push(load32(None)); // get signal offset (it is already the actual one in memory); + } else { + instructions.push(";; has indexes".to_string()); + instructions.push(tee_local(producer.get_io_info_tag())); + instructions.push(load32(None)); // get offset; first slot in io_info (to start adding offsets) + // if the first access is qualified we place the address of the bus_id on the stack + if let AccessType::Qualified(_) = &indexes[0] { + instructions.push(get_local(producer.get_io_info_tag())); + instructions.push(load32(Some("4"))); // it is a bus, so the bus_id is in the second position + } + let mut idxpos = 0; + while idxpos < indexes.len() { + if let AccessType::Indexed(index_info) = &indexes[idxpos] { + let index_list = &index_info.indexes; + let dim = index_info.symbol_dim; + let mut infopos = 0; + assert!(index_list.len() > 0); + //We first compute the number of elements as + //((index_list[0] * length_of_dim[1]) + index_list[1]) * length_of_dim[2] + ... )* length_of_dim[n-1] + index_list[n-1] + //first position in the array access + let mut instructions_idx0 = index_list[0].produce_wasm(producer); + instructions.append(&mut instructions_idx0); + for i in 1..index_list.len() { + instructions.push(get_local(producer.get_io_info_tag())); + infopos += 4; //position in io or bus info of dimension of [1] (recall that first dimension is not added) + instructions.push(load32(Some(&infopos.to_string()))); // second dimension + instructions.push(mul32()); + let mut instructions_idxi = index_list[i].produce_wasm(producer); + instructions.append(&mut instructions_idxi); + instructions.push(add32()); + } + assert!(index_list.len() <= dim); + let diff = dim - index_list.len(); + if diff > 0 { + //println!("There is difference: {}",diff); + //instructions.push(format!(";; There is a difference {}", diff)); + // must be last access + assert!(idxpos+1 == indexes.len()); + instructions.push(get_local(producer.get_io_info_tag())); + infopos += 4; //position in io or bus info of the next dimension + instructions.push(load32(Some(&infopos.to_string()))); // length of next dimension + for _i in 1..diff { + //instructions.push(format!(";; Next dim {}", i)); + instructions.push(get_local(producer.get_io_info_tag())); + infopos += 4; //position in io or bus info of the next dimension + instructions.push(load32(Some(&infopos.to_string()))); // length of next dimension + instructions.push(mul32()); // multiply with previous dimensions + } + } // after this we have the product of the remaining dimensions + let field_size = producer.get_size_32_bits_in_memory() * 4; + instructions.push(set_constant(&field_size.to_string())); + instructions.push(get_local(producer.get_io_info_tag())); + infopos += 4; //position in io or bus info of size + instructions.push(load32(Some(&infopos.to_string()))); // size + instructions.push(mul32()); // size mult by size of field in bytes + if diff > 0 { + //instructions.push(format!(";; Multiply dimensions")); + instructions.push(mul32()); // total size of the content according to the missing dimensions + } + instructions.push(mul32()); // total offset in the array + instructions.push(add32()); // to the current offset + idxpos += 1; + if idxpos < indexes.len() { + //next must be Qualified + if let AccessType::Indexed(_) = &indexes[idxpos] { + assert!(false); + } + // we add the type of bus it is + instructions.push(get_local(producer.get_io_info_tag())); + infopos += 4; + instructions.push(load32(Some(&infopos.to_string()))); // bus_id + } + } else if let AccessType::Qualified(field_no) = &indexes[idxpos] { + //we have on the stack the bus_id + instructions.push(set_constant("4")); //size in byte of i32 + instructions.push(mul32()); //maybe better in the memory like this + instructions.push(load32(Some( + &producer.get_bus_instance_to_field_start().to_string() + ))); // get position in the bus to field in memory + let field_no_bytes = field_no * 4; + instructions.push(load32(Some(&field_no_bytes.to_string()))); // get position in the field info in memory + if idxpos +1 < indexes.len() { + instructions.push(tee_local(producer.get_io_info_tag())); + } + //let field_size = producer.get_size_32_bits_in_memory() * 4; + //instructions.push(set_constant(&field_size.to_string())); + instructions.push(load32(None)); // get the offset + //instructions.push(mul32()); // mult by size of field in bytes + instructions.push(add32()); // add to the current offset + idxpos += 1; + if idxpos < indexes.len() { + if let AccessType::Qualified(_) = &indexes[idxpos] { + instructions.push(get_local(producer.get_io_info_tag())); + instructions.push(load32(Some("4"))); // bus_id + } + } + } else { + assert!(false); + } + } + } instructions.push(get_local(producer.get_sub_cmp_tag())); instructions.push(set_constant( &producer.get_signal_start_address_in_component().to_string(), )); instructions.push(add32()); - instructions.push(load32(None)); //subcomponent start_of_signals + instructions.push(load32(None)); //subcomponent start_of_signals: first info in the subcomponent instructions.push(add32()); // we get the position of the signal (with indexes) in memory } _ => { @@ -178,17 +272,64 @@ impl WriteWasm for StoreBucket { if producer.needs_comments() { instructions.push(";; getting src".to_string()); } - if self.context.size > 1 { - instructions.push(set_local(producer.get_store_aux_1_tag())); - } - let mut instructions_src = self.src.produce_wasm(producer); - instructions.append(&mut instructions_src); - if self.context.size == 1 { + if (!is_multiple_dest && size_dest == 1) || (!is_multiple_src && size_src == 1) { + //min to copy is 1 + let mut instructions_src = self.src.produce_wasm(producer); + instructions.append(&mut instructions_src); instructions.push(call("$Fr_copy")); } else { - instructions.push(set_local(producer.get_store_aux_2_tag())); - instructions.push(set_constant(&self.context.size.to_string())); - instructions.push(set_local(producer.get_copy_counter_tag())); + instructions.push(set_local(producer.get_store_aux_1_tag())); //set address destination + if !is_multiple_dest && !is_multiple_src { + instructions.push(set_constant(&std::cmp::min(&size_dest,&size_src).to_string())); + } else { + if is_multiple_dest { //create a nested if-else with all cases + instructions.push(get_local(producer.get_sub_cmp_tag())); + instructions.push(load32(None)); // get template id + instructions.push(set_local(producer.get_aux_0_tag())); + let mut instr_if = create_if_selection(&values_dest,producer.get_aux_0_tag()); + instructions.append(&mut instr_if); + } else { + instructions.push(set_constant(&size_dest.to_string())); + } + if is_multiple_src { //create a nested if-else with all cases + if self.src_address_type.is_some() { + instructions.push(get_local(producer.get_offset_tag())); + instructions.push(set_constant( + &producer.get_sub_component_start_in_component().to_string(), + )); + instructions.push(add32()); + let mut instr_cmp_src = self.src_address_type.as_ref().unwrap().produce_wasm(producer); + instructions.append(&mut instr_cmp_src); + instructions.push(set_constant("4")); //size in byte of i32 + instructions.push(mul32()); + instructions.push(add32()); + instructions.push(load32(None)); //subcomponent block + instructions.push(load32(None)); // get template id + instructions.push(set_local(producer.get_aux_0_tag())); + let mut instr_if = create_if_selection(&values_src,producer.get_aux_0_tag()); + instructions.append(&mut instr_if); + } else { + assert!(false); + } + } else { + instructions.push(set_constant(&size_src.to_string())); + } + instructions.push(tee_local(producer.get_aux_0_tag())); + instructions.push(tee_local(producer.get_aux_1_tag())); + instructions.push(lt32_u()); + instructions.push(format!("{} (result i32)", add_if())); + instructions.push(get_local(producer.get_aux_0_tag())); + instructions.push(add_else()); + instructions.push(get_local(producer.get_aux_1_tag())); + instructions.push(add_end()); + instructions.push(tee_local(producer.get_result_size_tag())); + } + instructions.push(set_local(producer.get_copy_counter_tag())); + + let mut instructions_src = self.src.produce_wasm(producer); // compute the address of the source + instructions.append(&mut instructions_src); + instructions.push(set_local(producer.get_store_aux_2_tag())); // set address source + instructions.push(add_block()); instructions.push(add_loop()); instructions.push(get_local(producer.get_copy_counter_tag())); @@ -225,7 +366,13 @@ impl WriteWasm for StoreBucket { instructions.push(load32(Some( &producer.get_input_counter_address_in_component().to_string(), ))); //remaining inputs to be set - instructions.push(set_constant(&self.context.size.to_string())); + if (!is_multiple_dest && size_dest == 1) || (!is_multiple_src && size_src == 1) { + instructions.push(set_constant("1"));} + else if !is_multiple_dest && !is_multiple_src { + instructions.push(set_constant(&std::cmp::min(&size_dest,&size_src).to_string())); + } else { + instructions.push(get_local(producer.get_result_size_tag())); + } instructions.push(sub32()); instructions.push(store32(Some( &producer.get_input_counter_address_in_component().to_string(), @@ -291,47 +438,131 @@ impl WriteWasm for StoreBucket { instructions } } - + impl WriteC for StoreBucket { fn produce_c(&self, producer: &CProducer, parallel: Option) -> (Vec, String) { use c_code_generator::*; let mut prologue = vec![]; let cmp_index_ref = "cmp_index_ref".to_string(); + let src_index_ref = "aux_src_index".to_string(); let aux_dest_index = "aux_dest_index".to_string(); + + //prologue.push(format!("// store bucket. Line {}", self.line)); //.to_string() + if let AddressType::SubcmpSignal { cmp_address, .. } = &self.dest_address_type { let (mut cmp_prologue, cmp_index) = cmp_address.produce_c(producer, parallel); prologue.append(&mut cmp_prologue); - prologue.push(format!("{{")); - prologue.push(format!("uint {} = {};", cmp_index_ref, cmp_index)); - } + prologue.push(format!("{{")); + prologue.push(format!("uint {} = {};", cmp_index_ref, cmp_index)); + } + if self.src_address_type.is_some() { + let (mut cmp_prologue, cmp_index) = self.src_address_type.as_ref().unwrap().produce_c(producer, parallel); + prologue.append(&mut cmp_prologue); + prologue.push(format!("uint {} = {};", src_index_ref, cmp_index)); + } + // We compute the possible sizes, case multiple sizes + let expr_size = match &self.context.size{ + SizeOption::Single(value) => value.to_string(), + SizeOption::Multiple(values) => { + prologue.push(format!("std::map size_store {};", + set_list_tuple(values.clone()) + )); + let sub_component_pos_in_memory = format!("{}[{}]",MY_SUBCOMPONENTS,cmp_index_ref); + let temp_id = template_id_in_component(sub_component_pos_in_memory); + format!("size_store[{}]", temp_id) + } + }; + // We compute the possible sizes for the src, case multiple sizes + let src_size = match &self.src_context.size{ + SizeOption::Single(value) => value.to_string(), + SizeOption::Multiple(values) => { + prologue.push(format!("std::map size_src_store {};", + set_list_tuple(values.clone()) + )); + let sub_component_pos_in_memory = format!("{}[{}]",MY_SUBCOMPONENTS,src_index_ref); + let temp_id = template_id_in_component(sub_component_pos_in_memory); + format!("size_src_store[{}]", temp_id) + } + }; + let size = match(&self.context.size, &self.src_context.size){ + (SizeOption::Single(v1), SizeOption::Single(v2)) =>{ + std::cmp::min(v1, v2).to_string() + }, + (_, _) => { + format!("std::min({}, {})", expr_size, src_size) + } + }; + let ((mut dest_prologue, dest_index), my_template_header) = if let LocationRule::Indexed { location, template_header } = &self.dest { (location.produce_c(producer, parallel), template_header.clone()) - } else if let LocationRule::Mapped { signal_code, indexes } = &self.dest { + } else if let LocationRule::Mapped { signal_code, indexes} = &self.dest { //if Mapped must be SubcmpSignal + //println!("Line {} is Mapped: {}",self.line, self.dest.to_string()); let mut map_prologue = vec![]; let sub_component_pos_in_memory = format!("{}[{}]",MY_SUBCOMPONENTS,cmp_index_ref.clone()); let mut map_access = format!("{}->{}[{}].defs[{}].offset", circom_calc_wit(), template_ins_2_io_info(), template_id_in_component(sub_component_pos_in_memory.clone()), signal_code.to_string()); - if indexes.len()>0 { - map_prologue.push(format!("{{")); - map_prologue.push(format!("uint map_index_aux[{}];",indexes.len().to_string())); - let (mut index_code_0, mut map_index) = indexes[0].produce_c(producer, parallel); - map_prologue.append(&mut index_code_0); - map_prologue.push(format!("map_index_aux[0]={};",map_index)); - map_index = format!("map_index_aux[0]"); - for i in 1..indexes.len() { - let (mut index_code, index_exp) = indexes[i].produce_c(producer, parallel); - map_prologue.append(&mut index_code); - map_prologue.push(format!("map_index_aux[{}]={};",i.to_string(),index_exp)); - map_index = format!("({})*{}->{}[{}].defs[{}].lengths[{}]+map_index_aux[{}]", - map_index, circom_calc_wit(), template_ins_2_io_info(), + if indexes.len() > 0 { + map_prologue.push(format!("{{")); + map_prologue.push(format!("uint map_accesses_aux[{}];",indexes.len().to_string())); + map_prologue.push(format!("{{")); + //cur_def contains a pointer to the definion of the next acces. + //The first time it is taken from template_ins_2_io_info + map_prologue.push(format!("IOFieldDef *cur_def = &({}->{}[{}].defs[{}]);", + circom_calc_wit(), template_ins_2_io_info(), template_id_in_component(sub_component_pos_in_memory.clone()), - signal_code.to_string(),(i-1).to_string(),i.to_string()); - } - map_access = format!("{}+{}",map_access,map_index); + signal_code.to_string())); + let mut idxpos = 0; + while idxpos < indexes.len() { + if let AccessType::Indexed(index_info) = &indexes[idxpos] { + let index_list = &index_info.indexes; + let dim = index_info.symbol_dim; + map_prologue.push(format!("{{")); + map_prologue.push(format!("uint map_index_aux[{}];",index_list.len().to_string())); + //We first compute the number of elements as + //((map_index_aux[0] * length_of_dim[1]) + map_index_aux[1]) * length_of_dim[2] + ... )* length_of_dim[n-1] + map_index_aux[n-1] with + // map_index_aux[i] = computation of index_list[i] + let (mut index_code_0, mut map_index) = index_list[0].produce_c(producer, parallel); + map_prologue.append(&mut index_code_0); + map_prologue.push(format!("map_index_aux[0]={};",map_index)); + map_index = format!("map_index_aux[0]"); + for i in 1..index_list.len() { + let (mut index_code, index_exp) = index_list[i].produce_c(producer, parallel); + map_prologue.append(&mut index_code); + map_prologue.push(format!("map_index_aux[{}]={};",i.to_string(),index_exp)); + map_index = format!("({})*cur_def->lengths[{}]+map_index_aux[{}]", + map_index,(i-1).to_string(),i.to_string()); + } + assert!(index_list.len() <= dim); + if dim - index_list.len() > 0 { + map_prologue.push(format!("//There is a difference {};",dim - index_list.len())); + // must be last access + assert!(idxpos+1 == indexes.len()); + for i in index_list.len()..dim { + map_index = format!("{}*cur_def->lengths[{}]", + map_index, (i-1).to_string()); + } // after this we have multiplied by the remaining dimensions + } + // multiply the offset in the array (after multiplying by the missing dimensions) by the size of the elements + map_prologue.push(format!("map_accesses_aux[{}] = {}*cur_def->size;", idxpos.to_string(), map_index)); + map_prologue.push(format!("}}")); + } else if let AccessType::Qualified(field_no) = &indexes[idxpos] { + map_prologue.push(format!("cur_def = &({}->{}[cur_def->busId].defs[{}]);", + circom_calc_wit(), bus_ins_2_field_info(), + field_no.to_string())); + map_prologue.push(format!("map_accesses_aux[{}] = cur_def->offset;", idxpos.to_string())); + } else { + assert!(false); + } + // add to the access expression the computed offset + map_access = format!("{}+map_accesses_aux[{}]", + map_access, idxpos.to_string()); + idxpos += 1; + } + map_prologue.push(format!("}}")); } ((map_prologue, map_access),Some(template_id_in_component(sub_component_pos_in_memory.clone()))) } else { @@ -372,13 +603,13 @@ impl WriteC for StoreBucket { prologue.append(&mut src_prologue); prologue.push(format!("// end load src")); std::mem::drop(src_prologue); - if self.context.size > 1 { - let copy_arguments = vec![aux_dest, src, self.context.size.to_string()]; + if size != "1" && size != "0" { + let copy_arguments = vec![aux_dest, src, size.clone()]; prologue.push(format!("{};", build_call("Fr_copyn".to_string(), copy_arguments))); if let AddressType::Signal = &self.dest_address_type { if parallel.unwrap() && self.dest_is_output { prologue.push(format!("{{")); - prologue.push(format!("for (int i = 0; i < {}; i++) {{",self.context.size)); + prologue.push(format!("for (int i = 0; i < {}; i++) {{", size)); prologue.push(format!("{}->componentMemory[{}].mutexes[{}+i].lock();",CIRCOM_CALC_WIT,CTX_INDEX,aux_dest_index.clone())); prologue.push(format!("{}->componentMemory[{}].outputIsSet[{}+i]=true;",CIRCOM_CALC_WIT,CTX_INDEX,aux_dest_index.clone())); prologue.push(format!("{}->componentMemory[{}].mutexes[{}+i].unlock();",CIRCOM_CALC_WIT,CTX_INDEX,aux_dest_index.clone())); @@ -411,7 +642,7 @@ impl WriteC for StoreBucket { ); let sub_cmp_counter_decrease = format!( "{} -= {}", - sub_cmp_counter, self.context.size + sub_cmp_counter, size ); if let InputInformation::Input{status} = input_information { if let StatusInput::NoLast = status { @@ -536,14 +767,14 @@ impl WriteC for StoreBucket { } _ => (), } - if let AddressType::SubcmpSignal { .. } = &self.dest_address_type { - prologue.push(format!("}}")); - } if let LocationRule::Mapped { indexes, .. } = &self.dest { if indexes.len() > 0 { prologue.push(format!("}}")); } } + if let AddressType::SubcmpSignal { .. } = &self.dest_address_type { + prologue.push(format!("}}")); + } (prologue, "".to_string()) } diff --git a/compiler/src/intermediate_representation/translate.rs b/compiler/src/intermediate_representation/translate.rs index 04309bb23..0860a1659 100644 --- a/compiler/src/intermediate_representation/translate.rs +++ b/compiler/src/intermediate_representation/translate.rs @@ -1,35 +1,47 @@ use super::ir_interface::*; use crate::hir::very_concrete_program::*; use crate::intermediate_representation::log_bucket::LogBucketArg; +use crate::intermediate_representation::types::SizeOption; use constant_tracking::ConstantTracker; use num_bigint_dig::BigInt; use program_structure::ast::*; use program_structure::file_definition::FileLibrary; use program_structure::utils::environment::VarEnvironment; use std::collections::{HashMap, BTreeMap, HashSet}; +use std::mem; type Length = usize; pub type E = VarEnvironment; pub type FieldTracker = ConstantTracker; + + #[derive(Clone)] pub struct SymbolInfo { access_instruction: InstructionPointer, dimensions: Vec, + size: usize, // needed, in case it is a bus to dont have to compute it again is_component: bool, + is_bus: bool, + bus_id: Option, } + #[derive(Clone)] -pub struct SignalInfo{ +pub struct WireInfo{ signal_type: SignalType, lengths: Vec, + size: usize, + bus_id: Option, // in case signal it is none } + + #[derive(Clone)] pub struct TemplateDB { // one per template instance pub signal_addresses: Vec, // stores the type and the length of signal - pub signal_info: Vec>, + pub wire_info: Vec>, // template_name to usize pub indexes: HashMap, // one per generic template, gives its signal to code correspondence @@ -40,7 +52,7 @@ impl TemplateDB { let mut database = TemplateDB { indexes: HashMap::with_capacity(templates.len()), signal_addresses: Vec::with_capacity(templates.len()), - signal_info: Vec::with_capacity(templates.len()), + wire_info: Vec::with_capacity(templates.len()), signals_id: Vec::with_capacity(templates.len()), }; for tmp in templates { @@ -58,13 +70,14 @@ impl TemplateDB { &db.signal_addresses[instance_id] } + fn add_instance(db: &mut TemplateDB, instance: &TemplateInstance) { if !db.indexes.contains_key(&instance.template_name) { let index = db.signals_id.len(); db.indexes.insert(instance.template_name.clone(), index); let mut correspondence = HashMap::new(); - for (id, signal) in instance.signals.iter().enumerate() { - correspondence.insert(signal.name.clone(), id); + for (id, signal) in instance.wires.iter().enumerate() { + correspondence.insert(signal.name().clone(), id); } db.signals_id.push(correspondence); } @@ -75,17 +88,36 @@ impl TemplateDB { HashMap::with_capacity(0), instance.signals_to_tags.clone(), ); - let mut signal_info = HashMap::new(); - for signal in instance.signals.clone() { - let info = SignalInfo{ signal_type: signal.xtype, lengths: signal.lengths}; - signal_info.insert(signal.name, info); + let mut wire_info = HashMap::new(); + for wire in &instance.wires { + match wire{ + Wire::TSignal(signal) =>{ + let info = WireInfo{ + size: signal.size, + signal_type: signal.xtype, + lengths: signal.lengths.clone(), + bus_id: None + }; + wire_info.insert(signal.name.clone(), info); + }, + Wire::TBus(bus) =>{ + let info = WireInfo{ + size: bus.size, + signal_type: bus.xtype, + lengths: bus.lengths.clone(), + bus_id: Some(bus.bus_id) + }; + wire_info.insert(bus.name.clone(), info); + } + } } - initialize_signals(&mut state, instance.signals.clone()); + initialize_signals(&mut state, instance.wires.clone()); db.signal_addresses.push(state.environment); - db.signal_info.push(signal_info); + db.wire_info.push(wire_info); } } + struct State { field_tracker: FieldTracker, environment: E, @@ -153,11 +185,12 @@ impl State { } struct Context<'a> { - translating: String, + _translating: String, files: &'a FileLibrary, tmp_database: &'a TemplateDB, - functions: &'a HashMap>, + _functions: &'a HashMap>, cmp_to_type: HashMap, + buses: &'a Vec } fn initialize_parameters(state: &mut State, params: Vec) { @@ -174,7 +207,14 @@ fn initialize_parameters(state: &mut State, params: Vec) { }; let address_instruction = address_instruction.allocate(); let symbol_info = - SymbolInfo { dimensions: lengths, access_instruction: address_instruction.clone(), is_component:false }; + SymbolInfo { + dimensions: lengths, + access_instruction: address_instruction.clone(), + is_component:false, + is_bus: false, + bus_id: None, + size: full_size + }; state.environment.add_variable(&p.name, symbol_info); } } @@ -193,7 +233,14 @@ fn initialize_constants(state: &mut State, constants: Vec) { } .allocate(); let symbol_info = - SymbolInfo { access_instruction: address_instruction.clone(), dimensions, is_component:false }; + SymbolInfo { + access_instruction: address_instruction.clone(), + dimensions, + is_component:false, + is_bus: false, + bus_id: None, + size + }; state.environment.add_variable(&arg.name, symbol_info); let mut index = 0; for value in arg.values { @@ -228,7 +275,9 @@ fn initialize_constants(state: &mut State, constants: Vec) { dest_is_output: false, dest_address_type: AddressType::Variable, dest: LocationRule::Indexed { location: full_address, template_header: None }, - context: InstrContext { size: 1 }, + context: InstrContext { size: SizeOption::Single(1) }, + src_context: InstrContext {size: SizeOption::Single(1)}, + src_address_type: None, src: content, } .allocate(); @@ -238,10 +287,24 @@ fn initialize_constants(state: &mut State, constants: Vec) { } } -fn initialize_signals(state: &mut State, signals: Vec) { - for signal in signals { - let size = signal.lengths.iter().fold(1, |p, c| p * (*c)); +fn initialize_signals(state: &mut State, wires: Vec) { + + for wire in wires{ + let size = wire.size(); let address = state.reserve_signal(size); + let dimensions = wire.lengths().clone(); + let name = wire.name().clone(); + let xtype = wire.xtype(); + + let (is_bus, bus_id) = match wire{ + Wire::TBus(bus) =>{ + (true, Some(bus.bus_id)) + }, + Wire::TSignal(_) =>{ + (false, None) + } + }; + let instruction = ValueBucket { line: 0, message_id: state.message_id, @@ -250,9 +313,17 @@ fn initialize_signals(state: &mut State, signals: Vec) { op_aux_no: 0, } .allocate(); - let info = SymbolInfo { access_instruction: instruction, dimensions: signal.lengths, is_component:false }; - state.environment.add_variable(&signal.name, info); - state.signal_to_type.insert(signal.name.clone(), signal.xtype); + let info = SymbolInfo { + access_instruction: instruction, + dimensions, + is_component:false, + is_bus, + bus_id, + size + }; + state.environment.add_variable(&name, info); + state.signal_to_type.insert(name.to_string(), xtype); + } } @@ -268,7 +339,14 @@ fn initialize_components(state: &mut State, components: Vec) { op_aux_no: 0, } .allocate(); - let info = SymbolInfo { access_instruction: instruction, dimensions: component.lengths, is_component: true }; + let info = SymbolInfo { + access_instruction: instruction, + dimensions: component.lengths, + is_component: true, + is_bus: false, + bus_id: None, + size + }; state.environment.add_variable(&component.name, info); } } @@ -534,8 +612,9 @@ fn translate_standard_case( state: &mut State, context: &Context, ) -> InstructionPointer { + let (src_size, src_address)= get_expression_size(&info.src, state, context); let src = translate_expression(info.src, state, context); - info.prc_symbol.into_store(src, state) + info.prc_symbol.into_store(src, state, src_size, src_address) } // End of substitution utils @@ -555,7 +634,14 @@ fn translate_declaration(stmt: Statement, state: &mut State, context: &Context) op_aux_no: 0, } .allocate(); - let info = SymbolInfo { access_instruction: instruction, dimensions, is_component: false }; + let info = SymbolInfo { + access_instruction: instruction, + dimensions, + is_component: false, + is_bus: false, + bus_id: None, + size + }; state.environment.add_variable(&name, info); } else { unreachable!() @@ -585,7 +671,11 @@ fn translate_constraint_equality(stmt: Statement, state: &mut State, context: &C let length = if let Variable { meta, name, access} = lhe.clone() { let def = SymbolDef { meta, symbol: name, acc: access }; - ProcessedSymbol::new(def, state, context).length + let aux = ProcessedSymbol::new(def, state, context).length; + match aux{ + SizeOption::Single(value) => value, + SizeOption::Multiple(_) => unreachable!("Should be single possible lenght"), + } } else {1}; let lhe_pointer = translate_expression(lhe, state, context); @@ -654,11 +744,17 @@ fn translate_log(stmt: Statement, state: &mut State, context: &Context) { fn translate_return(stmt: Statement, state: &mut State, context: &Context) { use Statement::Return; if let Return { meta, value, .. } = stmt { - let return_type = context.functions.get(&context.translating).unwrap(); + + let (src_size, _) = get_expression_size(&value, state, context); + // it is always a Single, not possible multiple options --> ENSURE + let with_size = match src_size{ + SizeOption::Single(v) => v, + SizeOption::Multiple(_) => unreachable!("Not possible multiple sizes"), + }; let return_bucket = ReturnBucket { line: context.files.get_line(meta.start, meta.get_file_id()).unwrap(), message_id: state.message_id, - with_size: return_type.iter().fold(1, |p, c| p * (*c)), + with_size, value: translate_expression(value, state, context), } .allocate(); @@ -762,7 +858,8 @@ fn check_tag_access(name_signal: &String, access: &Vec, state: &mut Stat let symbol_info = state.environment.get_variable(name_signal).unwrap().clone(); let mut value_tag = None; - if !symbol_info.is_component{ + // TODO: case tags of buses -> future work + if !symbol_info.is_component && !symbol_info.is_bus{ for acc in access { match acc { ArrayAccess(..) => {}, @@ -787,7 +884,7 @@ fn translate_variable( state: &mut State, context: &Context, ) -> InstructionPointer { - use Expression::{Variable}; + use Expression::Variable; if let Variable { meta, name, access, .. } = expression { let tag_access = check_tag_access(&name, &access, state); if tag_access.is_some(){ @@ -867,9 +964,12 @@ fn bigint_to_cid(field_tracker: &mut FieldTracker, big: &BigInt) -> usize { fn build_signal_location( signal: &str, cmp_name: &str, - indexes: Vec, + indexes: Vec>, context: &Context, state: &State, + dimensions: Vec, + size: usize, + bus_accesses: Vec, ) -> LocationRule { use ClusterType::*; let database = &context.tmp_database; @@ -877,13 +977,44 @@ fn build_signal_location( match cmp_type { Mixed { tmp_name } => { let signal_code = TemplateDB::get_signal_id(database, tmp_name, signal); - let indexes = indexing_instructions_filter(indexes, state); - LocationRule::Mapped { signal_code, indexes } + + let mut accesses = Vec::new(); + let mut i = 0; + let len_indexes = indexes.len(); + for index in indexes{ + let filtered = indexing_instructions_filter(index, state); + if filtered.len() > 0{ + let symbol_dim = if i == 0{ + dimensions.len() // dimensions is the length of the first symbol + } else{ + bus_accesses[i-1].lengths.len() // if not return length of the bus + }; + let index_info = IndexedInfo{ + indexes: filtered, + symbol_dim + }; + accesses.push(AccessType::Indexed(index_info)); + } + if i != len_indexes -1{ + // The last access is just an index + accesses.push(AccessType::Qualified(bus_accesses[i].field_id)); + } + i+=1; + } + LocationRule::Mapped { signal_code, indexes: accesses } } Uniform { instance_id, header, .. } => { let env = TemplateDB::get_instance_addresses(database, *instance_id); let location = env.get_variable(signal).unwrap().clone(); - let full_address = compute_full_address(state, location, indexes); + let full_address = compute_full_address( + state, + location.access_instruction, + dimensions, + size, + bus_accesses, + indexes, + + ); LocationRule::Indexed { location: full_address, template_header: Some(header.clone()) } } } @@ -895,93 +1026,290 @@ struct SymbolDef { acc: Vec, } +// It stores the possible lengths and sizes of the access +// --> Case heterogeneus components -> might be different +struct PossibleInfo{ + possible_sizes: Vec, + possible_lengths: Vec>, + possible_bus_fields: Option>>, + possible_cmp_id: Vec +} + +struct BusAccessInfo{ + offset: usize, + field_id: usize, + size: usize, + lengths: Vec +} + struct ProcessedSymbol { line: usize, - length: usize, + length: SizeOption, + symbol_dimensions: Vec, // the dimensions of last symbol + symbol_size: usize, // the size of the last symbol message_id: usize, name: String, symbol: SymbolInfo, xtype: TypeReduction, signal: Option, signal_type: Option, - before_signal: Vec, + before_signal: Vec>, + // in case it is a bus indicate the bus accesses info + bus_accesses: Vec, } impl ProcessedSymbol { fn new(definition: SymbolDef, state: &mut State, context: &Context) -> ProcessedSymbol { use Access::*; + + // Getting the symbol info let symbol_name = definition.symbol; let meta = definition.meta; let symbol_info = state.environment.get_variable(&symbol_name).unwrap().clone(); - let mut lengths = symbol_info.dimensions.clone(); - lengths.reverse(); - let mut with_length = symbol_info.dimensions.iter().fold(1, |r, c| r * (*c)); - let mut signal = None; let mut signal_type = state.signal_to_type.get(&symbol_name).cloned(); - let mut bf_index = vec![]; - let mut af_index = vec![]; - let mut multiple_possible_lengths: Vec> = vec![]; + let mut is_bus = symbol_info.is_bus; + let mut is_component = symbol_info.is_component; + let mut accessed_component_signal = None; + + // Initializing the status (single case by now) + let mut length = symbol_info.dimensions.clone(); + length.reverse(); + let bus_fields = if symbol_info.bus_id.is_some(){ + let id = symbol_info.bus_id.unwrap(); + Some(vec![context.buses.get(id).unwrap().fields.clone()]) + } else{ + None + }; + let mut possible_status = PossibleInfo{ + possible_lengths: vec![length], + possible_sizes: vec![symbol_info.size], + possible_bus_fields: bus_fields, + possible_cmp_id: vec![0], + }; + + // Arrays to store the index accesses (before and after the component access) + let mut before_index: Vec = vec![]; // indexes accessed before component + let mut after_indexes: Vec> = vec![]; // indexes accessed after component (or no component) + // we groud the same bus accesses in same position + let mut current_index: Vec = vec![]; // current accesses, updating now + + // Information about the current and accessed fields + let mut accessed_fields_info: Vec = Vec::new(); + let mut initial_symbol_size = symbol_info.size; + let mut initial_symbol_dimensions = symbol_info.dimensions.clone(); + for acc in definition.acc { match acc { - ArrayAccess(exp) if signal.is_none() => { - let length = lengths.pop().unwrap(); - with_length /= length; - bf_index.push(translate_expression(exp, state, context)); - } + ArrayAccess(exp) => { - for possible_length in &mut multiple_possible_lengths{ - possible_length.pop(); + // we need to study all possible sizes and lenghts + let mut index = 0; + for possible_length in &mut possible_status.possible_lengths{ + let aux_length = possible_length.pop(); + possible_status.possible_sizes[index] /= aux_length.unwrap(); + index += 1; } - af_index.push(translate_expression(exp, state, context)); + + current_index.push(translate_expression(exp, state, context)); } + ComponentAccess(name) => { - let possible_cmp_id = state.component_to_instance.get(&symbol_name).unwrap().clone(); - for cmp_id in possible_cmp_id{ - let aux = context.tmp_database.signal_info[cmp_id].get(&name).unwrap(); - signal_type = Some(aux.signal_type); - let mut new_length = aux.lengths.clone(); - new_length.reverse(); - multiple_possible_lengths.push(new_length); + // we distinguish the cases component and bus + if is_component{ + + let possible_cmp_id = state.component_to_instance.get(&symbol_name).unwrap().clone(); + let mut is_first = true; + + // We init the possible lenghts and sizes + possible_status.possible_lengths = Vec::new(); + possible_status.possible_sizes = Vec::new(); + possible_status.possible_cmp_id = Vec::new(); + + for cmp_id in possible_cmp_id{ + // aux contains the info about the accessed wire + let aux = context.tmp_database.wire_info[cmp_id].get(&name).unwrap(); + signal_type = Some(aux.signal_type); + // update the possible status + let mut new_length = aux.lengths.clone(); + new_length.reverse(); + possible_status.possible_lengths.push(new_length); + possible_status.possible_sizes.push(aux.size); + possible_status.possible_cmp_id.push(cmp_id); + if aux.bus_id.is_some(){ + let fields = context.buses.get(aux.bus_id.unwrap()).unwrap().fields.clone(); + if is_first{ + is_bus = true; + possible_status.possible_bus_fields = Some(vec![fields]); + } else{ + possible_status.possible_bus_fields.as_mut().unwrap().push(fields); + } + + } else{ + if is_first{ + is_bus = false; + possible_status.possible_bus_fields = None; + } + } + + if is_first{ + // this will be used in the case of + // homogeneus component + initial_symbol_size = aux.size; + initial_symbol_dimensions = aux.lengths.clone(); + is_first = false + } + + } + + // The current indexes are before index + assert!(after_indexes.len() == 0); + + before_index = std::mem::take(&mut current_index); + + is_component = false; + accessed_component_signal = Some(name); + + } else if is_bus{ + + // set to new to start the size check again + let old_possible_fields = mem::take(&mut possible_status.possible_bus_fields.unwrap()); + possible_status.possible_lengths = vec![]; + possible_status.possible_sizes = vec![]; + possible_status.possible_bus_fields = None; // Just to have an init + + let mut is_first = true; + + // check each one of the options for the field sizes + for possible_fields in old_possible_fields{ + let field_info = possible_fields.get(&name).unwrap(); + let mut new_length = field_info.dimensions.clone(); + new_length.reverse(); + possible_status.possible_lengths.push(new_length); + possible_status.possible_sizes.push(field_info.size); + + if field_info.bus_id.is_some(){ + let id = field_info.bus_id.unwrap(); + let fields = context.buses.get(id).unwrap().fields.clone(); + if is_first{ + possible_status.possible_bus_fields = Some(vec![fields]); + } else{ + possible_status.possible_bus_fields.as_mut().unwrap().push(fields); + } + } else{ + possible_status.possible_bus_fields = None; + } + if is_first{ + is_bus = field_info.bus_id.is_some(); + accessed_fields_info.push({ + BusAccessInfo{ + offset: field_info.offset, + field_id: field_info.field_id, + size: field_info.size, + lengths: field_info.dimensions.clone() + } + }); + + is_first = false; + } + } + + + // We move the current index into the after_indexes + let aux_index = std::mem::take(&mut current_index); + after_indexes.push(aux_index); + } else{ + unreachable!() } - signal = Some(name); + } } } - if signal.is_some(){ + + // We add the latest indexes into after_indexes + let aux_index = std::mem::take(&mut current_index); + after_indexes.push(aux_index); + + + if accessed_component_signal.is_some(){ + // Case accessing a io signal of a subcomponent + + // First check that the possible sizes are all equal let mut is_first = true; - for possible_length in multiple_possible_lengths{ + let mut all_equal = true; + let mut with_length: usize = 0; + + let mut multiple_sizes = vec![]; + let mut index = 0; + + for possible_size in &possible_status.possible_sizes{ if is_first{ - with_length = possible_length.iter().fold(1, |r, c| r * (*c)); + with_length = *possible_size; is_first = false; } else{ - if with_length != possible_length.iter().fold(1, |r, c| r * (*c)){ - unreachable!("On development: Circom compiler does not accept for now the assignment of arrays of unknown sizes during the execution of loops"); + if with_length != *possible_size{ + all_equal = false; } } + multiple_sizes.push((possible_status.possible_cmp_id[index], *possible_size)); + index += 1; } - } - let signal_location = signal.map(|signal_name| { - build_signal_location( - &signal_name, + let size = if all_equal{ + SizeOption::Single(with_length) + } else{ + SizeOption::Multiple(multiple_sizes) + }; + + // Compute the signal location inside the component + let signal_location = build_signal_location( + &accessed_component_signal.unwrap(), &symbol_name, - af_index, + after_indexes, context, state, - ) - }); - ProcessedSymbol { - xtype: meta.get_type_knowledge().get_reduces_to(), - line: context.files.get_line(meta.start, meta.get_file_id()).unwrap(), - message_id: state.message_id, - length: with_length, - symbol: symbol_info, - name: symbol_name, - before_signal: bf_index, - signal: signal_location, - signal_type + initial_symbol_dimensions, + initial_symbol_size, + accessed_fields_info, + + ); + + // compute the component location + ProcessedSymbol { + xtype: meta.get_type_knowledge().get_reduces_to(), + line: context.files.get_line(meta.start, meta.get_file_id()).unwrap(), + message_id: state.message_id, + length: size, + symbol_dimensions: symbol_info.dimensions.clone(), + symbol_size: symbol_info.size, + symbol: symbol_info, + name: symbol_name, + before_signal: vec![before_index], + signal: Some(signal_location), + signal_type, + bus_accesses: Vec::new() + } + } else{ + + assert!(possible_status.possible_sizes.len() == 1); + let with_length: usize = possible_status.possible_sizes[0]; + + ProcessedSymbol { + xtype: meta.get_type_knowledge().get_reduces_to(), + line: context.files.get_line(meta.start, meta.get_file_id()).unwrap(), + message_id: state.message_id, + length: SizeOption::Single(with_length), + symbol_dimensions: initial_symbol_dimensions, + symbol_size: initial_symbol_size, + symbol: symbol_info, + name: symbol_name, + before_signal: after_indexes, + signal: None, + signal_type, + bus_accesses: accessed_fields_info + } } + } fn into_call_assign( @@ -992,7 +1320,14 @@ impl ProcessedSymbol { ) -> InstructionPointer { let data = if let Option::Some(signal) = self.signal { let dest_type = AddressType::SubcmpSignal { - cmp_address: compute_full_address(state, self.symbol, self.before_signal), + cmp_address: compute_full_address( + state, + self.symbol.access_instruction, + self.symbol_dimensions, + self.symbol_size, + self.bus_accesses, + self.before_signal, + ), is_output: self.signal_type.unwrap() == SignalType::Output, uniform_parallel_value: state.component_to_parallel.get(&self.name).unwrap().uniform_parallel_value, input_information : match self.signal_type.unwrap() { @@ -1007,8 +1342,14 @@ impl ProcessedSymbol { dest: signal, } } else { - let address = compute_full_address(state, self.symbol, self.before_signal); - let xtype = match self.xtype { + let address = compute_full_address( + state, + self.symbol.access_instruction, + self.symbol_dimensions, + self.symbol_size, + self.bus_accesses, + self.before_signal, + ); let xtype = match self.xtype { TypeReduction::Variable => AddressType::Variable, _ => AddressType::Signal, }; @@ -1031,10 +1372,23 @@ impl ProcessedSymbol { .allocate() } - fn into_store(self, src: InstructionPointer, state: &State) -> InstructionPointer { + fn into_store( + self, src: + InstructionPointer, + state: &State, + src_size: SizeOption, + src_address: Option + ) -> InstructionPointer { if let Option::Some(signal) = self.signal { let dest_type = AddressType::SubcmpSignal { - cmp_address: compute_full_address(state, self.symbol, self.before_signal), + cmp_address: compute_full_address( + state, + self.symbol.access_instruction, + self.symbol_dimensions, + self.symbol_size, + self.bus_accesses, + self.before_signal, + ), uniform_parallel_value: state.component_to_parallel.get(&self.name).unwrap().uniform_parallel_value, is_output: self.signal_type.unwrap() == SignalType::Output, input_information : match self.signal_type.unwrap() { @@ -1048,12 +1402,21 @@ impl ProcessedSymbol { line: self.line, message_id: self.message_id, context: InstrContext { size: self.length }, + src_context: InstrContext {size: src_size}, dest_is_output: false, dest_address_type: dest_type, + src_address_type: src_address } .allocate() } else { - let address = compute_full_address(state, self.symbol, self.before_signal); + let address = compute_full_address( + state, + self.symbol.access_instruction, + self.symbol_dimensions, + self.symbol_size, + self.bus_accesses, + self.before_signal, + ); let xtype = match self.xtype { TypeReduction::Variable => AddressType::Variable, _ => AddressType::Signal, @@ -1066,6 +1429,8 @@ impl ProcessedSymbol { dest_is_output: self.signal_type.map_or(false, |t| t == SignalType::Output), dest: LocationRule::Indexed { location: address, template_header: None }, context: InstrContext { size: self.length }, + src_context: InstrContext {size: src_size}, + src_address_type: src_address } .allocate() } @@ -1074,7 +1439,14 @@ impl ProcessedSymbol { fn into_load(self, state: &State) -> InstructionPointer { if let Option::Some(signal) = self.signal { let dest_type = AddressType::SubcmpSignal { - cmp_address: compute_full_address(state, self.symbol, self.before_signal), + cmp_address: compute_full_address( + state, + self.symbol.access_instruction, + self.symbol_dimensions, + self.symbol_size, + self.bus_accesses, + self.before_signal, + ), uniform_parallel_value: state.component_to_parallel.get(&self.name).unwrap().uniform_parallel_value, is_output: self.signal_type.unwrap() == SignalType::Output, input_information : match self.signal_type.unwrap() { @@ -1091,7 +1463,14 @@ impl ProcessedSymbol { } .allocate() } else { - let address = compute_full_address(state, self.symbol, self.before_signal); + let address = compute_full_address( + state, + self.symbol.access_instruction, + self.symbol_dimensions, + self.symbol_size, + self.bus_accesses, + self.before_signal, + ); let xtype = match self.xtype { TypeReduction::Variable => AddressType::Variable, _ => AddressType::Signal, @@ -1110,20 +1489,67 @@ impl ProcessedSymbol { fn compute_full_address( state: &State, - symbol: SymbolInfo, - indexed_with: Vec, + symbol_access_instr: InstructionPointer, + mut dimensions: Vec, // for each one of the bus accesses one dimensions + size: usize, // each one of the field sizes + bus_accesses: Vec, + indexed_with: Vec>, // each one of the accesses ) -> InstructionPointer { - if symbol.dimensions.is_empty() { - symbol.access_instruction - } else { - let at = symbol.access_instruction; - let mut with_dimensions = symbol.dimensions; - with_dimensions.reverse(); - let mut linear_length = with_dimensions.iter().fold(1, |p, c| p * (*c)); - let index_stack = indexing_instructions_filter(indexed_with, state); - let mut stack = vec![]; + + let at = symbol_access_instr; + let mut stack = vec![]; + + + let number_bus_access = bus_accesses.len(); + assert!(number_bus_access == indexed_with.len() - 1); + + // add the initial indexing + dimensions.reverse(); + let mut linear_length = size; + let index_stack = indexing_instructions_filter(indexed_with[0].clone(), state); + for instruction in index_stack { + let dimension_length = dimensions.pop().unwrap(); + linear_length /= dimension_length; + let inst = ValueBucket { + line: at.get_line(), + message_id: at.get_message_id(), + parse_as: ValueType::U32, + op_aux_no: 0, + value: linear_length, + } + .allocate(); + let jump = ComputeBucket { + line: at.get_line(), + message_id: at.get_message_id(), + op_aux_no: 0, + op: OperatorType::MulAddress, + stack: vec![inst, instruction], + } + .allocate(); + stack.push(jump); + } + + let mut index = 1; + + + for mut access in bus_accesses{ + + if access.offset != 0{ + let offset_bucket = ValueBucket { + line: at.get_line(), + message_id: at.get_message_id(), + parse_as: ValueType::U32, + op_aux_no: 0, + value: access.offset, + }.allocate(); + stack.push(offset_bucket); + } + + access.lengths.reverse(); + let mut linear_length = access.size; + let index_stack = indexing_instructions_filter(indexed_with[index].clone(), state); for instruction in index_stack { - let dimension_length = with_dimensions.pop().unwrap(); + let dimension_length = access.lengths.pop().unwrap(); linear_length /= dimension_length; let inst = ValueBucket { line: at.get_line(), @@ -1143,9 +1569,12 @@ fn compute_full_address( .allocate(); stack.push(jump); } - stack.push(at); - fold(OperatorType::AddAddress, stack, state) + + index += 1; } + + stack.push(at); + fold(OperatorType::AddAddress, stack, state) } fn indexing_instructions_filter( @@ -1341,12 +1770,75 @@ fn translate_call_arguments( .iter() .fold(1, |r, c| r * (*c)); let instr = translate_expression(arg, state, context); - info.argument_data.push(InstrContext { size: length }); + info.argument_data.push(InstrContext { size: SizeOption::Single(length) }); info.arguments.push(instr); } info } +/******** Auxiliar functions to get the size of an expression ************/ + +fn get_expression_size(expression: &Expression, state: &mut State, context: &Context) + -> (SizeOption, Option){ + if expression.is_infix() { + (SizeOption::Single(1), None) + } else if expression.is_prefix() { + (SizeOption::Single(1), None) + } else if expression.is_variable() { + get_variable_size(expression, state, context) + } else if expression.is_number() { + (SizeOption::Single(1), None) + } else if expression.is_call() { + unreachable!("This case should be unreachable") + } else if expression.is_array() { + unreachable!("This expression is syntactic sugar") + } else if expression.is_switch() { + unreachable!("This expression is syntactic sugar") + } else { + unreachable!("Unknown expression") + } +} + +fn get_variable_size( + expression: &Expression, + state: &mut State, + context: &Context, +) -> (SizeOption, Option) { + use Expression::Variable; + if let Variable { meta, name, access, .. } = expression { + let tag_access = check_tag_access(&name, &access, state); + if tag_access.is_some(){ + (SizeOption::Single(1), None) + } else{ + let def = SymbolDef { meta: meta.clone(), symbol: name.clone(), acc: access.clone() }; + let aux_symbol = ProcessedSymbol::new(def, state, context); + + let size = aux_symbol.length; + let possible_address = match size{ + SizeOption::Multiple(_)=>{ + let address = compute_full_address( + state, + aux_symbol.symbol.access_instruction, + aux_symbol.symbol_dimensions, + aux_symbol.symbol_size, + aux_symbol.bus_accesses, + aux_symbol.before_signal, + ); + Some(address) + }, + SizeOption::Single(_) => None + }; + (size, possible_address) + } + } else { + unreachable!() + } +} + + +/*************************************************************/ + + pub struct ParallelClusters{ pub positions_to_parallel: BTreeMap, bool>, pub uniform_parallel_value: Option, @@ -1356,7 +1848,7 @@ pub struct CodeInfo<'a> { pub header: String, pub message_id: usize, pub params: Vec, - pub signals: Vec, + pub wires: Vec, pub files: &'a FileLibrary, pub constants: Vec, pub components: Vec, @@ -1370,6 +1862,7 @@ pub struct CodeInfo<'a> { pub component_to_parallel: HashMap, pub string_table: HashMap, pub signals_to_tags: BTreeMap, + pub buses: &'a Vec } pub struct CodeOutput { @@ -1393,16 +1886,17 @@ pub fn translate_code(body: Statement, code_info: CodeInfo) -> CodeOutput { ); state.string_table = code_info.string_table; initialize_components(&mut state, code_info.components); - initialize_signals(&mut state, code_info.signals); + initialize_signals(&mut state, code_info.wires); initialize_constants(&mut state, code_info.constants); initialize_parameters(&mut state, code_info.params); let context = Context { files: code_info.files, - translating: code_info.header, - functions: code_info.functions, + _translating: code_info.header, + _functions: code_info.functions, cmp_to_type: code_info.cmp_to_type, tmp_database: code_info.template_database, + buses: code_info.buses }; create_components(&mut state, &code_info.triggers, code_info.clusters); @@ -1424,3 +1918,5 @@ pub fn translate_code(body: Statement, code_info: CodeInfo) -> CodeOutput { string_table : state.string_table } } + + diff --git a/compiler/src/intermediate_representation/types.rs b/compiler/src/intermediate_representation/types.rs index 7968e214d..95b490031 100644 --- a/compiler/src/intermediate_representation/types.rs +++ b/compiler/src/intermediate_representation/types.rs @@ -14,7 +14,13 @@ impl ToString for ValueType { } } -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq)] +pub enum SizeOption{ + Single(usize), + Multiple(Vec<(usize, usize)>) // The first value indicates the cmp_id, the second the size +} + +#[derive(Clone, PartialEq, Eq)] pub struct InstrContext { - pub size: usize, + pub size: SizeOption, } diff --git a/compiler/src/ir_processing/build_inputs_info.rs b/compiler/src/ir_processing/build_inputs_info.rs index 49e6edb9b..d16f48df3 100644 --- a/compiler/src/ir_processing/build_inputs_info.rs +++ b/compiler/src/ir_processing/build_inputs_info.rs @@ -96,9 +96,14 @@ pub fn visit_call( inside_loop: bool )-> bool { use ReturnType::*; - if let Final(data) = &mut bucket.return_info { - if data.context.size > 0{ + let needs_consider = match data.context.size{ + SizeOption::Single(value) if value == 0 =>{ + false + } + _ => true + }; + if needs_consider{ visit_address_type( &mut data.dest_address_type, known_last_component, @@ -216,7 +221,13 @@ pub fn visit_store( found_unknown_address: bool, inside_loop: bool )-> bool{ - if bucket.context.size > 0{ + let needs_consider = match bucket.context.size{ + SizeOption::Single(value) if value == 0 =>{ + false + } + _ => true + }; + if needs_consider{ visit_address_type( &mut bucket.dest_address_type, known_last_component, diff --git a/compiler/src/ir_processing/build_stack.rs b/compiler/src/ir_processing/build_stack.rs index e7ad33471..7310a5529 100644 --- a/compiler/src/ir_processing/build_stack.rs +++ b/compiler/src/ir_processing/build_stack.rs @@ -144,17 +144,27 @@ pub fn build_value(bucket: &mut ValueBucket, fresh: usize) -> usize { pub fn build_location(bucket: &mut LocationRule, mut fresh: usize) -> (usize, usize) { use LocationRule::*; match bucket { - Indexed { location, .. } => - build_instruction_address(location, fresh), + Indexed { location, .. } => build_instruction_address(location, fresh), Mapped { indexes, .. } => { let mut max_stack = fresh; - for i in indexes{ - let (depth, new_fresh) = build_instruction_address(i, fresh); - max_stack = std::cmp::max(max_stack, depth); - fresh = new_fresh; + for acc in indexes{ + match acc{ + AccessType::Indexed(ind) =>{ + for i in &mut ind.indexes{ + let (depth, new_fresh) = build_instruction_address(i, fresh); + max_stack = std::cmp::max(max_stack, depth); + fresh = new_fresh; + } + }, + AccessType::Qualified(_) =>{ + + } + } + } (max_stack, fresh) } + } } diff --git a/compiler/src/ir_processing/reduce_stack.rs b/compiler/src/ir_processing/reduce_stack.rs index c59bed223..0723f681e 100644 --- a/compiler/src/ir_processing/reduce_stack.rs +++ b/compiler/src/ir_processing/reduce_stack.rs @@ -159,15 +159,31 @@ pub fn reduce_location_rule(lc: LocationRule) -> LocationRule { let location = Allocate::allocate(reduce_instruction(*location)); Indexed { location, template_header } } - Mapped { signal_code, indexes } => { - let no_indexes = InstructionList::len(&indexes); - let work = indexes; - let mut indexes = InstructionList::with_capacity(no_indexes); - for index in work { - let index = Allocate::allocate(reduce_instruction(*index)); - InstructionList::push(&mut indexes, index); + Mapped { signal_code, indexes: accesses } => { + let no_accesses = accesses.len(); + let work_accesses = accesses; + let mut accesses = Vec::with_capacity(no_accesses); + for acc in work_accesses{ + match acc{ + AccessType::Indexed(indexes) =>{ + let no_indexes = InstructionList::len(&indexes.indexes); + let work = indexes.indexes; + let symbol_dim = indexes.symbol_dim; + let mut indexes = InstructionList::with_capacity(no_indexes); + for index in work { + let index = Allocate::allocate(reduce_instruction(*index)); + InstructionList::push(&mut indexes, index); + } + accesses.push(AccessType::Indexed(IndexedInfo{indexes, symbol_dim})); + } + AccessType::Qualified(_) =>{ + accesses.push(acc); + + } + } } - Mapped { signal_code, indexes } + + Mapped { signal_code, indexes: accesses } } } } diff --git a/compiler/src/ir_processing/set_arena_size.rs b/compiler/src/ir_processing/set_arena_size.rs index 8d772b8e0..b80b37a7a 100644 --- a/compiler/src/ir_processing/set_arena_size.rs +++ b/compiler/src/ir_processing/set_arena_size.rs @@ -98,10 +98,21 @@ pub fn visit_store(bucket: &mut StoreBucket, function_to_arena_size: &HashMap) {} pub fn visit_location(bucket: &mut LocationRule, function_to_arena_size: &HashMap) { - use LocationRule::*; + match bucket { - Indexed { location, .. } => visit_instruction(location, function_to_arena_size), - Mapped { indexes, .. } => visit_list(indexes, function_to_arena_size), + LocationRule::Indexed { location, .. } => visit_instruction(location, function_to_arena_size), + LocationRule::Mapped { indexes, .. } => { + for access in indexes{ + match access{ + AccessType::Indexed(instr) =>{ + visit_list(&mut instr.indexes, function_to_arena_size) + }, + AccessType::Qualified(_) =>{ + + } + } + } + } } } diff --git a/compiler/src/lib.rs b/compiler/src/lib.rs index a9c4871e0..d7f2352ac 100644 --- a/compiler/src/lib.rs +++ b/compiler/src/lib.rs @@ -1,6 +1,6 @@ #[allow(dead_code)] -mod circuit_design; -mod intermediate_representation; +pub mod circuit_design; +pub mod intermediate_representation; mod ir_processing; pub extern crate num_bigint_dig as num_bigint; pub extern crate num_traits; diff --git a/constraint_generation/Cargo.toml b/constraint_generation/Cargo.toml index faef4dd43..cfbc6a7a9 100644 --- a/constraint_generation/Cargo.toml +++ b/constraint_generation/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "constraint_generation" -version = "2.1.9" +version = "2.2.0" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/constraint_generation/src/assignment_utils.rs b/constraint_generation/src/assignment_utils.rs new file mode 100644 index 000000000..dd886ac47 --- /dev/null +++ b/constraint_generation/src/assignment_utils.rs @@ -0,0 +1,200 @@ +use super::environment_utils:: + + slice_types::{MemoryError, TypeAssignmentError, + SignalSlice, SliceCapacity, TagInfo, TagState, TagDefinitions, + BusSlice + }; + +use std::mem; + + +// Utils for assigning tags + +pub fn compute_propagated_tags(tags_values: &TagInfo, tags_definitions: &TagDefinitions)-> TagInfo{ + let mut tags_propagated = TagInfo::new(); + for (tag, value) in tags_values{ + let state = tags_definitions.get(tag).unwrap(); + if state.value_defined || state.complete{ + tags_propagated.insert(tag.clone(), value.clone()); + } else if state.defined{ + tags_propagated.insert(tag.clone(), None); + } + } + tags_propagated +} + +pub fn perform_tag_propagation(tags_values: &mut TagInfo, tags_definitions: &mut TagDefinitions, assigned_tags: &TagInfo, is_init: bool){ + // Study the tags: add the new ones and copy their content. + /* + Cases: + + Inherance in arrays => We only have a tag in case it inherites the tag in all positions + + - Tag defined by user: + * Already with value defined by user => do not copy new values + * No value defined by user + - Already initialized: + * If same value as previous preserve + * If not set value to None + - No initialized: + * Set value to new one + - Tag not defined by user: + * Already initialized: + - If contains same tag with same value preserve + - No tag or different value => do not save tag or loose it + * No initialized: + - Save tag + + + */ + let previous_tags = mem::take(tags_values); + + for (tag, value) in previous_tags{ + let tag_state = tags_definitions.get(&tag).unwrap(); + if tag_state.defined{// is signal defined by user + if tag_state.value_defined{ + // already with value, store the same value + tags_values.insert(tag, value); + } else{ + if is_init { + // only keep value if same as previous + let to_store_value = if assigned_tags.contains_key(&tag){ + let value_new = assigned_tags.get(&tag).unwrap(); + if value != *value_new{ + None + } else{ + value + } + } else{ + None + }; + tags_values.insert(tag, to_store_value); + } else{ + // always keep + if assigned_tags.contains_key(&tag){ + let value_new = assigned_tags.get(&tag).unwrap(); + tags_values.insert(tag, value_new.clone()); + } else{ + tags_values.insert(tag, None); + } + } + } + } else{ + // it is not defined by user + if assigned_tags.contains_key(&tag){ + let value_new = assigned_tags.get(&tag).unwrap(); + if value == *value_new{ + tags_values.insert(tag, value); + } else{ + tags_values.remove(&tag); + } + } else{ + tags_values.remove(&tag); + } + } + } + + if !is_init{ // first init, add new tags + for (tag, value) in assigned_tags{ + if !tags_values.contains_key(tag){ // in case it is a new tag (not defined by user) + tags_values.insert(tag.clone(), value.clone()); + let state = TagState{defined: false, value_defined: false, complete: false}; + tags_definitions.insert(tag.clone(), state); + } + } + } + +} + + +pub fn perform_signal_assignment(signal_slice: &mut SignalSlice, array_access: &[SliceCapacity], new_route: &[SliceCapacity])-> Result<(), MemoryError>{ + let memory_response_for_signal_previous_value = SignalSlice::access_values( + signal_slice, + array_access, + ); + let signal_previous_value = match memory_response_for_signal_previous_value{ + Ok(v) => v, + Err(err) => return Err(err) + }; + + let new_value_slice = &SignalSlice::new_with_route(new_route, &true); + + let correct_dims_result = SignalSlice::check_correct_dims( + &signal_previous_value, + &Vec::new(), + &new_value_slice, + true + ); + match correct_dims_result{ + Ok(_) => {}, + Err(err) => return Err(err) + }; + + for i in 0..SignalSlice::get_number_of_cells(&signal_previous_value){ + let memory_response_access = SignalSlice::access_value_by_index(&signal_previous_value, i); + let signal_was_assigned = match memory_response_access{ + Ok(v) => v, + Err(err) => return Err(err) + }; + if signal_was_assigned { + return Result::Err(MemoryError::AssignmentError(TypeAssignmentError::MultipleAssignments)); + } + } + + + + let access_response = SignalSlice::insert_values( + signal_slice, + array_access, + &new_value_slice, + true + ); + + match access_response{ + Ok(_) => {}, + Err(err) => return Err(err) + }; + Result::Ok(()) +} + + +pub fn perform_bus_assignment(bus_slice: &mut BusSlice, array_access: &[SliceCapacity], assigned_bus_slice: &BusSlice, is_input: bool)-> Result<(), MemoryError>{ + + let correct_dims_result = BusSlice::check_correct_dims( + &bus_slice, + &array_access, + &assigned_bus_slice, + true + ); + match correct_dims_result{ + Ok(_) => {}, + Err(err) => return Err(err) + }; + + + let value_left = match BusSlice::access_values_by_mut_reference(bus_slice, array_access){ + Ok(value) => value, + Err(err) => return Err(err) + }; + + + + let mut index = 0; + for accessed_bus in value_left{ + // We completely assign each one of them + let memory_response_assign = BusSlice::get_reference_to_single_value_by_index(&assigned_bus_slice, index); + let assigned_bus = match memory_response_assign{ + Ok(v) => v, + Err(err) => return Err(err) + }; + + match accessed_bus.completely_assign_bus(&assigned_bus, is_input){ + Ok(_) =>{}, + Err(err) => return Err(err) + }; + index += 1; + + } + + Ok(()) +} diff --git a/constraint_generation/src/compute_constants.rs b/constraint_generation/src/compute_constants.rs index d16020959..36d9c740c 100644 --- a/constraint_generation/src/compute_constants.rs +++ b/constraint_generation/src/compute_constants.rs @@ -151,6 +151,7 @@ fn treat_declaration(stmt: &mut Statement, context: &Context, reports: &mut Repo AnonymousComponent => { meta.get_mut_memory_knowledge().set_concrete_dimensions(vec![]); }, + _ => { for d in dimensions.iter_mut() { let execution_response = treat_dimension(d, context, reports, flags, prime); diff --git a/constraint_generation/src/environment_utils/bus_representation.rs b/constraint_generation/src/environment_utils/bus_representation.rs new file mode 100644 index 000000000..502ecaa86 --- /dev/null +++ b/constraint_generation/src/environment_utils/bus_representation.rs @@ -0,0 +1,637 @@ + +use super::slice_types::{BusSlice, FieldTypes, FoldedArgument, FoldedResult, MemoryError, SignalSlice, SliceCapacity, TagDefinitions, TagInfo, TagState, TypeAssignmentError}; +use crate::execution_data::type_definitions::{NodePointer, AccessingInformationBus}; +use crate::execution_data::ExecutedProgram; +use std::collections::{BTreeMap,HashMap}; +use crate::ast::Meta; + +use crate::assignment_utils::*; + +pub struct BusRepresentation { + pub node_pointer: Option, + pub meta: Option, + fields: BTreeMap, + pub field_tags: BTreeMap, + unassigned_fields: HashMap, + has_assignment: bool, +} + +impl Default for BusRepresentation { + fn default() -> Self { + BusRepresentation { + node_pointer: Option::None, + fields: BTreeMap::new(), + field_tags: BTreeMap::new(), + meta: Option::None, + unassigned_fields: HashMap::new(), + has_assignment: false + } + } +} +impl Clone for BusRepresentation { + fn clone(&self) -> Self { + BusRepresentation { + node_pointer: self.node_pointer, + fields: self.fields.clone(), + field_tags: self.field_tags.clone(), + meta : self.meta.clone(), + unassigned_fields: self.unassigned_fields.clone(), + has_assignment: self.has_assignment + } + } +} + +impl BusRepresentation { + + + pub fn initialize_bus( + component: &mut BusRepresentation, + node_pointer: NodePointer, + scheme: &ExecutedProgram, + is_initialized: bool, + ) -> Result<(), MemoryError> { + let possible_node = ExecutedProgram::get_bus_node(scheme, node_pointer); + assert!(possible_node.is_some()); + let node = possible_node.unwrap(); + + + // if input bus all signals are set initialize to true, else to false + if is_initialized{ + component.has_assignment = true; + } + // initialice the signals + for info_field in node.fields() { + let symbol = &info_field.name; + let route = &info_field.length; + if !info_field.is_bus{ + let signal_slice = SignalSlice::new_with_route(&route, &is_initialized); + let signal_slice_size = SignalSlice::get_number_of_cells(&signal_slice); + if signal_slice_size > 0{ + component.unassigned_fields + .insert(symbol.clone(), signal_slice_size); + } + let field_signal = FieldTypes::Signal(signal_slice); + component.fields.insert(symbol.clone(), field_signal); + } else{ + let bus_connexions = node.bus_connexions(); + let bus_node = bus_connexions.get(symbol).unwrap().inspect.goes_to; + let mut bus_field = BusRepresentation::default(); + BusRepresentation::initialize_bus( + &mut bus_field, + bus_node, + scheme, + is_initialized + )?; + let bus_slice = BusSlice::new_with_route(&route, &bus_field); + let bus_slice_size = BusSlice::get_number_of_cells(&bus_slice); + if bus_slice_size > 0{ + component.unassigned_fields + .insert(symbol.clone(), bus_slice_size); + } + let field_bus = FieldTypes::Bus(bus_slice); + component.fields.insert(symbol.clone(), field_bus); + } + + + // add the tags + if node.signal_to_tags.get(symbol).is_some(){ + let defined_tags = node.signal_to_tags.get(symbol).unwrap(); + let mut definitions = BTreeMap::new(); + let mut values = BTreeMap::new(); + for (tag, value) in defined_tags{ + let tag_state = TagState{defined:true, value_defined: value.is_some(), complete: true}; + definitions.insert(tag.clone(), tag_state); + values.insert(tag.clone(), value.clone()); + + } + component.field_tags.insert(symbol.clone(), (definitions, values)); + } else{ + component.field_tags.insert(symbol.clone(), (BTreeMap::new(), BTreeMap::new())); + } + if is_initialized{ + component.unassigned_fields.remove(symbol); + } + } + + + component.node_pointer = Option::Some(node_pointer); + + + Result::Ok(()) + } + + pub fn get_field( + &self, + field_name: &str, + remaining_access: &AccessingInformationBus + ) -> Result<(Option, FoldedResult), MemoryError>{ + + let field = self.fields.get(field_name).unwrap(); + let (tags_defs, tags_info) = self.field_tags.get(field_name).unwrap(); + if remaining_access.field_access.is_some(){ + // we are still considering an intermediate bus or a tag, check cases + let next_access = remaining_access.field_access.as_ref().unwrap(); + if tags_info.contains_key(next_access){ + // case tag, return its value + + // access only allowed when (1) it is value defined by user or (2) it is completely assigned + let state = tags_defs.get(next_access).unwrap(); + if state.value_defined || state.complete{ + let value_tag = tags_info.get(next_access).unwrap(); + match value_tag{ + Option::None =>{ + let error = MemoryError::TagValueNotInitializedAccess; + Result::Err(error) + }, + Some(v) =>{ + let folded_tag = FoldedResult::Tag(v.clone()); + Result::Ok((None, folded_tag)) + } + } + } else{ + let error = MemoryError::TagValueNotInitializedAccess; + Result::Err(error) + } + } else{ + // case bus, access to the next field + match field{ + FieldTypes::Bus(bus_slice)=>{ + + let memory_response = BusSlice::access_values_by_reference( + &bus_slice, + &remaining_access.array_access + ); + match memory_response{ + Result::Ok(bus_slice) =>{ + assert!(bus_slice.len() == 1); + let resulting_bus = bus_slice[0]; + resulting_bus.get_field( + remaining_access.field_access.as_ref().unwrap(), + &remaining_access.remaining_access.as_ref().unwrap() + ) + } + Result::Err(err)=>{ + return Err(err); + } + } + } + FieldTypes::Signal(_) => unreachable!(), + } + } + + } else{ + // in this case there is no need for recursion, final access + + match field{ + FieldTypes::Signal(signal_slice) =>{ + // Case it is just a signal or an array of signals, + // in this case there is no need for recursion + + // compute which tags are propagated + let propagated_tags = compute_propagated_tags(tags_info, tags_defs); + + let accessed_slice_result = SignalSlice::access_values(&signal_slice, &remaining_access.array_access); + match accessed_slice_result{ + Ok(slice) =>{ + let folded_slice = FoldedResult::Signal(slice); + Result::Ok((Some(propagated_tags), folded_slice)) + }, + Err(err) => Err(err) + } + } + FieldTypes::Bus(bus_slice) => { + // Case it is just a bus or an array of buses, + // in this case there is no need for recursion + + // compute which tags are propagated + let propagated_tags = compute_propagated_tags(tags_info, tags_defs); + + let accessed_slice_result = BusSlice::access_values(&bus_slice, &remaining_access.array_access); + match accessed_slice_result{ + Ok(slice) =>{ + let folded_slice = FoldedResult::Bus(slice); + Result::Ok((Some(propagated_tags), folded_slice)) + }, + Err(err) => Err(err) + } + }, + } + } + } + + + + pub fn has_unassigned_fields(&self) -> bool{ + self.node_pointer.is_none() || !self.unassigned_fields.is_empty() + } + + pub fn assign_value_to_field( + &mut self, + field_name: &str, + remaining_access: &AccessingInformationBus, + assigned_value: FoldedArgument, + tags: Option, + is_input: bool, + ) -> Result<(), MemoryError> { + // TODO: move to auxiliar function to do not repeat effort + // We update the has_assignment value if not tag and not empty + let has_assignment = match assigned_value{ + FoldedArgument::Signal(dimensions)=>{ + let total_size = dimensions.iter().fold(1, |acc, x| acc * x); + total_size > 0 + }, + FoldedArgument::Bus(slice)=>{ + let route = slice.route(); + let total_size = route.iter().fold(1, |acc, x| acc * x); + total_size > 0 + }, + FoldedArgument::Tag(_) => false + }; + if has_assignment{ + self.has_assignment = true; + } + + // We later distinguish the case of tags + // check if we need to access to another bus or if it is the final access + let field: &mut FieldTypes = self.fields.get_mut(field_name).unwrap(); + let (status_tags, info_tags) = self.field_tags.get_mut(field_name).unwrap(); + + if remaining_access.field_access.is_some(){ + // case still intermediate access or a tag + let next_access = remaining_access.field_access.as_ref().unwrap(); + + // Distinguish between tag and intermediate access + if info_tags.contains_key(next_access){ + + // it is tag assignment -> check if valid + match field{ + FieldTypes::Signal(s) =>{ + let signal_is_init = SignalSlice::get_number_of_inserts(&s) > 0; + if signal_is_init{ + return Result::Err(MemoryError::AssignmentTagAfterInit) + } + } + FieldTypes::Bus(s) =>{ + for i in 0..BusSlice::get_number_of_cells(s){ + let accessed_bus = BusSlice::get_reference_to_single_value_by_index(&s, i)?; + if accessed_bus.has_assignment(){ + return Result::Err(MemoryError::AssignmentTagAfterInit) + } + } + } + } + // Get the assigned value + let value = match assigned_value{ + FoldedArgument::Tag(value) =>{ + value + }, + _ => unreachable!() + }; + + let possible_tag = info_tags.get_mut(next_access); + if let Some(val) = possible_tag { + if let Some(_) = val { + // already assigned value, return error + Result::Err(MemoryError::AssignmentTagTwice) + } else { // we add the info saying that the tag is defined + let tag_state = status_tags.get_mut(next_access).unwrap(); + tag_state.value_defined = true; + *val = Option::Some(value.clone()); + Result::Ok(()) + } + } else{ + unreachable!() + } + } else{ + // it is intermediate access + + match field{ + FieldTypes::Bus(bus_slice)=>{ + // case bus -> apply recursion + + // no tags assigned to the complete bus + // Check in case input if it is expecting tags, if so return error + if is_input{ + if !info_tags.is_empty(){ + let (possible_tag, _) = info_tags.iter().next().unwrap(); + return Result::Err(MemoryError::AssignmentMissingTags(field_name.to_string(), possible_tag.clone())); + } + } + + + let memory_response = BusSlice::access_values_by_mut_reference( + bus_slice, + &remaining_access.array_access + ); + match memory_response{ + Result::Ok(mut bus_slice) =>{ + assert!(bus_slice.len() == 1); + let resulting_bus = bus_slice.get_mut(0).unwrap(); + resulting_bus.assign_value_to_field( + remaining_access.field_access.as_ref().unwrap(), + &remaining_access.remaining_access.as_ref().unwrap(), + assigned_value, + tags, + is_input + )?; + + // Update from unassigned if it is completely assigned + if !resulting_bus.has_unassigned_fields(){ + match self.unassigned_fields.get_mut(field_name){ + Some(left) => { + *left -= 1; + if *left == 0 { + self.unassigned_fields.remove(field_name); + } + } + Option::None => {} + } + } + Result::Ok(()) + } + Result::Err(err)=>{ + return Err(err); + } + } + } + FieldTypes::Signal(_) => { + // no possible, already checked in check_types + unreachable!() + } + } + } + + } else{ + // case final assignment of signal or bus + let tags = tags.unwrap(); + + // first propagate the tags or check if conditions satisfied if input + let is_init = match field{ + FieldTypes::Signal(signal_slice) =>{ + SignalSlice::get_number_of_inserts(&signal_slice) > 0 + }, + FieldTypes::Bus(bus_slice) =>{ + let mut bus_is_init = false; + for i in 0..BusSlice::get_number_of_cells(bus_slice){ + match BusSlice::get_reference_to_single_value_by_index(bus_slice, i){ + Ok(bus) => { + bus_is_init |= bus.has_assignment(); + } + Err(_) => unreachable!() + } + } + bus_is_init + } + }; + if !is_input{ + // case no input, just propagate + perform_tag_propagation(info_tags, status_tags, &tags, is_init); + } else{ + // in case input check if tags are satisfied + for (t, value) in info_tags{ + if !tags.contains_key(t){ + return Result::Err(MemoryError::AssignmentMissingTags(field_name.to_string(), t.clone())); + } else{ + if !is_init{ + // First assignment of input tag + *value = tags.get(t).unwrap().clone(); + } + else{ + // already given a value, check that it is the same + // if not return error + if value != tags.get(t).unwrap(){ + return Result::Err(MemoryError::AssignmentTagInputTwice(field_name.to_string(), t.clone())); + } + } + } + } + } + + // then assign the values to the signal or bus + match field{ + FieldTypes::Signal(signal_slice) =>{ + let route = match assigned_value{ + FoldedArgument::Signal(signal_slice_route) =>{ + signal_slice_route + }, + _ => unreachable!() + }; + perform_signal_assignment(signal_slice, &remaining_access.array_access, route)?; + }, + FieldTypes::Bus(bus_slice) =>{ + let assigned_bus_slice = match assigned_value{ + FoldedArgument::Bus(bus_slice) =>{ + bus_slice + }, + _ => unreachable!() + }; + perform_bus_assignment(bus_slice, &remaining_access.array_access, assigned_bus_slice, is_input)?; + + } + } + + // Update the value of unnasigned fields + let slice_route = match assigned_value{ + FoldedArgument::Signal(signal_slice_route) =>{ + signal_slice_route + }, + FoldedArgument::Bus(bus_slice) =>{ + bus_slice.route() + }, + _ => unreachable!() + }; + + let mut dim_slice = 1; + for i in slice_route { + dim_slice *= *i; + } + + match self.unassigned_fields.get_mut(field_name){ + Some(left) => { + *left -= dim_slice; + if *left == 0 { + self.unassigned_fields.remove(field_name); + } + } + Option::None => {} + } + + // Update the value of the signal tags it is complete + + let is_completely_initialized = match field{ + FieldTypes::Signal(signal_slice) =>{ + SignalSlice::get_number_of_inserts(signal_slice) == + SignalSlice::get_number_of_cells(signal_slice) + }, + FieldTypes::Bus(bus_slice) =>{ + let mut bus_is_completely_init = true; + for i in 0..BusSlice::get_number_of_cells(bus_slice){ + match BusSlice::get_reference_to_single_value_by_index(bus_slice, i){ + Ok(bus) => { + bus_is_completely_init &= bus.has_assignment(); + } + Err(_) => unreachable!() + } + } + bus_is_completely_init + } + + }; + + if is_completely_initialized && !is_input{ + + for (_tag, state) in status_tags{ + state.complete = true; + } + } + Ok(()) + } + + } + + pub fn completely_assign_bus(&mut self, assigned_bus: &BusRepresentation, is_input: bool)-> Result<(), MemoryError>{ + + if self.has_assignment{ + return Result::Err(MemoryError::AssignmentError(TypeAssignmentError::MultipleAssignmentsBus)); + } + + // check that they are the same instance of buses + if self.node_pointer != assigned_bus.node_pointer{ + return Result::Err(MemoryError::AssignmentError(TypeAssignmentError::DifferentBusInstances)); + } + + self.has_assignment = true; + for (field_name, value) in &mut self.fields{ + + // get the tags that are propagated + let (tags_definition, tags_info) = self.field_tags.get_mut(field_name).unwrap(); + let (tags_assigned_definition, tags_assigned_info) = assigned_bus.field_tags.get(field_name).unwrap(); + let tags_propagated = compute_propagated_tags(tags_assigned_info, tags_assigned_definition); + + let is_init = false; + + // perform the tag assignment + if !is_input{ + // case no input, just propagate + perform_tag_propagation(tags_info, tags_definition, &tags_propagated, is_init); + } else{ + + // in case input check if tags are satisfied + for (t, value) in tags_info{ + if !tags_propagated.contains_key(t){ + return Result::Err(MemoryError::AssignmentMissingTags(field_name.to_string(), t.clone())); + } else{ + // Not needed check, always not init, if not error + // First assignment of input tag + *value = tags_propagated.get(t).unwrap().clone(); + } + } + } + + // perform the assignment + match value{ + FieldTypes::Bus(ref mut bus_slice) =>{ + + let bus_slice_assigned = match assigned_bus.fields.get(field_name).unwrap(){ + FieldTypes::Bus(bs) => bs, + FieldTypes::Signal(_) => unreachable!(), + }; + + let assignment_result = perform_bus_assignment(bus_slice, &[], bus_slice_assigned, is_input); + + if assignment_result.is_err(){ + return Err(assignment_result.err().unwrap()); + } + }, + FieldTypes::Signal(signal_slice)=>{ + // check if not assigned yet + // set to true + // updated unassigned_fields + + let new_value_slice = &SignalSlice::new_with_route(signal_slice.route(), &true); + + // : Not needed because we know that it has not been assigned? + // let dim_slice: usize = SignalSlice::get_number_of_cells(signal_slice); + // for i in 0..dim_slice{ + // let signal_was_assigned = match SignalSlice::access_value_by_index(&signal_slice, i){ + // Ok(v) => v, + // Err(_) => unreachable!() + // }; + // if signal_was_assigned { + // return Result::Err(MemoryError::AssignmentError(TypeAssignmentError::MultipleAssignments)); + // } + // } + + SignalSlice::insert_values( + signal_slice, + &Vec::new(), + &new_value_slice, + true + )?; + } + + } + + // Update the value of unnasigned fields + self.unassigned_fields.remove(field_name); + + // Update the value of the complete tags + for (_tag, state) in tags_definition{ + state.complete = true; + } + } + Ok(()) + + } + + pub fn get_accesses_bus(&self, name: &str) -> Vec{ + + fn unfold_signals(current: String, dim: usize, lengths: &[usize], result: &mut Vec) { + if dim == lengths.len() { + result.push(current); + } else { + for i in 0..lengths[dim] { + unfold_signals(format!("{}[{}]", current, i), dim + 1, lengths, result) + } + } + } + + let mut result = Vec::new(); + for field in &self.fields{ + match field{ + (field_name, FieldTypes::Bus(bus_slice)) => { + let accessed_name = format!("{}.{}", name, field_name); + let dims = bus_slice.route(); + let mut prefixes = Vec::new(); + unfold_signals(accessed_name, 0, dims, &mut prefixes); + for i in 0..BusSlice::get_number_of_cells(&bus_slice){ + let access = BusSlice::get_reference_to_single_value_by_index(&bus_slice, i); + + match access{ + Ok(bus) =>{ + let mut result_field = bus.get_accesses_bus(&prefixes[i]); + result.append(&mut result_field); + } + Err(_) =>{ + unreachable!() + } + } + } + } + (field_name, FieldTypes::Signal(signal_slice)) =>{ + let accessed_name = format!("{}.{}", name, field_name); + let dims = signal_slice.route(); + unfold_signals(accessed_name, 0, dims, &mut result); + } + } + } + result + } + + + + pub fn has_assignment(&self)-> bool{ + self.has_assignment + } + +} diff --git a/constraint_generation/src/environment_utils/component_representation.rs b/constraint_generation/src/environment_utils/component_representation.rs index 3d6505466..7da0dd663 100644 --- a/constraint_generation/src/environment_utils/component_representation.rs +++ b/constraint_generation/src/environment_utils/component_representation.rs @@ -1,9 +1,12 @@ -use super::slice_types::{MemoryError, TypeInvalidAccess, TypeAssignmentError, SignalSlice, SliceCapacity,TagInfo}; -use crate::execution_data::type_definitions::NodePointer; +use super::slice_types::{FoldedResult, FoldedArgument, BusSlice, MemoryError, SignalSlice, SliceCapacity, TagInfo, TypeAssignmentError, TypeInvalidAccess}; +use crate::execution_data::type_definitions::AccessingInformationBus; +use crate::{environment_utils::slice_types::BusRepresentation, execution_data::type_definitions::NodePointer}; use crate::execution_data::ExecutedProgram; use std::collections::{BTreeMap,HashMap, HashSet}; use crate::ast::Meta; +use crate::assignment_utils::*; + pub struct ComponentRepresentation { pub node_pointer: Option, pub is_parallel: bool, @@ -11,9 +14,13 @@ pub struct ComponentRepresentation { unassigned_inputs: HashMap, unassigned_tags: HashSet, to_assign_inputs: Vec<(String, Vec, Vec)>, + to_assign_input_buses: Vec<(String, Vec, BusSlice)>, + to_assign_input_bus_fields: Vec<(String, AccessingInformationBus, FoldedResult, TagInfo)>, inputs: HashMap, + input_buses: HashMap, pub inputs_tags: BTreeMap, outputs: HashMap, + output_buses: HashMap, pub outputs_tags: BTreeMap, pub is_initialized: bool, } @@ -32,6 +39,10 @@ impl Default for ComponentRepresentation { outputs_tags: BTreeMap::new(), is_initialized: false, meta: Option::None, + to_assign_input_buses: Vec::new(), + input_buses: HashMap::new(), + output_buses: HashMap::new(), + to_assign_input_bus_fields: Vec::new(), } } } @@ -49,6 +60,10 @@ impl Clone for ComponentRepresentation { outputs_tags: self.outputs_tags.clone(), is_initialized: self.is_initialized, meta : self.meta.clone(), + to_assign_input_buses: self.to_assign_input_buses.clone(), + input_buses: self.input_buses.clone(), + output_buses: self.output_buses.clone(), + to_assign_input_bus_fields: self.to_assign_input_bus_fields.clone(), } } } @@ -63,7 +78,7 @@ impl ComponentRepresentation { meta: &Meta, ) -> Result<(), MemoryError>{ if !is_anonymous_component && component.is_preinitialized() { - return Result::Err(MemoryError::AssignmentError(TypeAssignmentError::MultipleAssignments)); + return Result::Err(MemoryError::AssignmentError(TypeAssignmentError::MultipleAssignmentsComponent)); } let possible_node = ExecutedProgram::get_prenode(scheme, prenode_pointer); assert!(possible_node.is_some()); @@ -103,6 +118,10 @@ impl ComponentRepresentation { is_initialized: false, is_parallel, meta: Some(meta.clone()), + to_assign_input_buses: Vec::new(), + input_buses: HashMap::new(), + output_buses: HashMap::new(), + to_assign_input_bus_fields: Vec::new(), }; Result::Ok(()) } @@ -117,19 +136,38 @@ impl ComponentRepresentation { let node = possible_node.unwrap(); component.is_initialized = true; - for (symbol, route) in node.inputs() { - let signal_slice = SignalSlice::new_with_route(route, &false); - let signal_slice_size = SignalSlice::get_number_of_cells(&signal_slice); - if signal_slice_size > 0{ - component.unassigned_inputs - .insert(symbol.clone(), signal_slice_size); + for info_wire in node.inputs() { + let symbol = &info_wire.name; + let route = &info_wire.length; + if !info_wire.is_bus{ + let signal_slice = SignalSlice::new_with_route(route, &false); + let signal_slice_size = SignalSlice::get_number_of_cells(&signal_slice); + if signal_slice_size > 0{ + component.unassigned_inputs + .insert(symbol.clone(), signal_slice_size); + } + component.inputs.insert(symbol.clone(), signal_slice); + } else{ + let mut initial_value_bus = BusRepresentation::default(); + let bus_node = node.bus_connexions.get(symbol).unwrap().inspect.goes_to; + BusRepresentation::initialize_bus( + &mut initial_value_bus, + bus_node, + scheme, + false // it is not initialized at the begining + )?; + let bus_slice = BusSlice::new_with_route(route, &initial_value_bus); + let bus_slice_size = BusSlice::get_number_of_cells(&bus_slice); + if bus_slice_size > 0{ + component.unassigned_inputs + .insert(symbol.clone(), bus_slice_size); + } + component.input_buses.insert(symbol.clone(), bus_slice); } - component.inputs.insert(symbol.clone(), signal_slice); } - for (symbol, route) in node.outputs() { - component.outputs.insert(symbol.clone(), SignalSlice::new_with_route(route, &true)); - + + fn insert_tags_output(node: &crate::execution_data::ExecutedTemplate, symbol: &String, component: &mut ComponentRepresentation) { let tags_output = node.signal_to_tags.get(symbol); let component_tags_output = component.outputs_tags.get_mut(symbol); if tags_output.is_some() && component_tags_output.is_some(){ @@ -143,77 +181,176 @@ impl ComponentRepresentation { } } } - component.node_pointer = Option::Some(node_pointer); - let to_assign = component.to_assign_inputs.clone(); - for s in to_assign{ - let tags_input = component.inputs_tags.get(&s.0).unwrap(); - ComponentRepresentation::assign_value_to_signal_init(component, &s.0, &s.1, &s.2, tags_input.clone())?; - } - Result::Ok(()) - } -/* - pub fn signal_has_value( - component: &ComponentRepresentation, - signal_name: &str, - access: &[SliceCapacity], - ) -> Result { - if component.node_pointer.is_none() { - return Result::Err(MemoryError::InvalidAccess); - } - if component.outputs.contains_key(signal_name) && !component.unassigned_inputs.is_empty() { - return Result::Err(MemoryError::InvalidAccess); + for info_wire in node.outputs() { + let symbol = &info_wire.name; + let route = &info_wire.length; + if !info_wire.is_bus{ + component.outputs.insert(symbol.clone(), SignalSlice::new_with_route(route, &true)); + } else{ + let mut initial_value_bus = BusRepresentation::default(); + let bus_node = node.bus_connexions.get(symbol).unwrap().inspect.goes_to; + BusRepresentation::initialize_bus( + &mut initial_value_bus, + bus_node, + scheme, + true // the outputs of the component are initialized at the begining + )?; + let bus_slice = BusSlice::new_with_route(route, &initial_value_bus); + + component.output_buses.insert(symbol.clone(), bus_slice); + } + insert_tags_output(node, symbol, component); } - if !component.is_initialized{ - return Result::Err(MemoryError::InvalidAccess); + + component.node_pointer = Option::Some(node_pointer); + + let to_assign = std::mem::replace(&mut component.to_assign_inputs, vec![]); + + for (signal_name, access, route) in &to_assign{ + let tags_input = component.inputs_tags[signal_name].clone(); + component.assign_value_to_signal_init(signal_name, access, route, &tags_input)?; } - let slice = if component.inputs.contains_key(signal_name) { - component.inputs.get(signal_name).unwrap() - } else { - component.outputs.get(signal_name).unwrap() - }; + let to_assign = std::mem::replace(&mut component.to_assign_input_buses, vec![]); + for (signal_name, access, bus_slice) in &to_assign{ + let tags_input = component.inputs_tags[signal_name].clone(); + component.assign_value_to_bus_init(signal_name, access, bus_slice, &tags_input)?; + } - let enabled_slice = SignalSlice::access_values(&slice, &access)?; - let mut enabled = false; - for i in 0..SignalSlice::get_number_of_cells(&enabled_slice) { - enabled |= SignalSlice::get_reference_to_single_value_by_index(&enabled_slice, i)?; + let to_assign = std::mem::replace(&mut component.to_assign_input_bus_fields, vec![]); + for (signal_name, access, field_value, tags_input) in to_assign{ + component.assign_value_to_bus_field_init(&signal_name, &access, &field_value, tags_input)?; } - Result::Ok(enabled) + + Result::Ok(()) } -*/ - pub fn get_signal(&self, signal_name: &str) -> Result<(&TagInfo, &SignalSlice), MemoryError> { + fn check_initialized_inputs(&self, bus_name: &str) -> Result<(), MemoryError> { if self.node_pointer.is_none() { return Result::Err(MemoryError::InvalidAccess(TypeInvalidAccess::NoInitializedComponent)); } - if self.outputs.contains_key(signal_name) && !self.unassigned_inputs.is_empty() { + // in case it is an output signal or bus + if (self.outputs.contains_key(bus_name) || self.output_buses.contains_key(bus_name)) && !self.unassigned_inputs.is_empty() { // we return the name of an input that has not been assigned let ex_signal = self.unassigned_inputs.iter().next().unwrap().0.clone(); return Result::Err(MemoryError::InvalidAccess(TypeInvalidAccess::MissingInputs(ex_signal))); } - + if !self.is_initialized { // we return the name of an input with tags that has not been assigned let ex_signal = self.unassigned_tags.iter().next().unwrap().clone(); return Result::Err(MemoryError::InvalidAccess(TypeInvalidAccess::MissingInputTags(ex_signal))); } + Result::Ok(()) + } + + pub fn get_io_value(&self, field_name: &str, remaining_access: &AccessingInformationBus) ->Result<(Option, FoldedResult), MemoryError>{ + if let Result::Err(value) = self.check_initialized_inputs(field_name) { + return Err(value); + } + + if self.inputs.contains_key(field_name) || self.outputs.contains_key(field_name){ + // in this case we are accessing a signal + let (tag_info, signal_slice) = if self.inputs.contains_key(field_name) { + (self.inputs_tags.get(field_name).unwrap(), self.inputs.get(field_name).unwrap()) + } else { + (self.outputs_tags.get(field_name).unwrap(), self.outputs.get(field_name).unwrap()) + }; + + if remaining_access.field_access.is_some(){ + // in case it is a tag access + assert!(remaining_access.array_access.len() == 0); + let value_tag = tag_info.get(remaining_access.field_access.as_ref().unwrap()).unwrap(); + match value_tag{ + Option::None =>{ + let error = MemoryError::TagValueNotInitializedAccess; + Result::Err(error) + }, + Some(v) =>{ + let folded_tag = FoldedResult::Tag(v.clone()); + Result::Ok((None, folded_tag)) + } + } + } else{ + // case signals + // We access to the selected signal if it is an array + let accessed_slice_result = SignalSlice::access_values(signal_slice, &remaining_access.array_access); + match accessed_slice_result{ + Ok(slice) =>{ + let folded_slice = FoldedResult::Signal(slice); + Result::Ok((Some(tag_info.clone()), folded_slice)) + }, + Err(err) => Err(err) + } + } + } else{ + // in this case we are accessing a bus + let (tag_info, bus_slice) = if self.input_buses.contains_key(field_name) { + (self.inputs_tags.get(field_name).unwrap(), self.input_buses.get(field_name).unwrap()) + } else { + (self.outputs_tags.get(field_name).unwrap(), self.output_buses.get(field_name).unwrap()) + }; + + if remaining_access.field_access.is_some(){ + // In this case we need to access to values of the bus or one of its tags + let next_array_access = &remaining_access.array_access; + let next_field_access = remaining_access.field_access.as_ref().unwrap(); + let next_remaining_access = remaining_access.remaining_access.as_ref().unwrap(); + + // we distingish between tags or buses + if tag_info.contains_key(remaining_access.field_access.as_ref().unwrap()){ + // in this case we are returning a tag + assert!(next_array_access.len() == 0); + let value_tag = tag_info.get(next_field_access).unwrap(); + match value_tag{ + Option::None =>{ + let error = MemoryError::TagValueNotInitializedAccess; + Result::Err(error) + }, + Some(v) =>{ + let folded_tag = FoldedResult::Tag(v.clone()); + Result::Ok((None, folded_tag)) + } + } + } else{ + // in this case we are returning a field of the bus + + let accessed_slice_result = BusSlice::access_values(bus_slice, &remaining_access.array_access); + let accessed_bus = match accessed_slice_result{ + Ok(slice) =>{ + BusSlice::unwrap_to_single(slice) + }, + Err(err) => return Err(err) + }; + accessed_bus.get_field(next_field_access, next_remaining_access) + } + } else{ + + // In this case we are accessing the complete bus + let accessed_slice_result = BusSlice::access_values(bus_slice, &remaining_access.array_access); + + match accessed_slice_result{ + Ok(slice) =>{ + let folded_slice = FoldedResult::Bus(slice); + Result::Ok((Some(tag_info.clone()), folded_slice)) + }, + Err(err) => Err(err) + } + } + } - let slice = if self.inputs.contains_key(signal_name) { - (self.inputs_tags.get(signal_name).unwrap(), self.inputs.get(signal_name).unwrap()) - } else { - (self.outputs_tags.get(signal_name).unwrap(), self.outputs.get(signal_name).unwrap()) - }; - Result::Ok(slice) } + // Assign signals: Operations to assign signals -> case init and no init + pub fn assign_value_to_signal( component: &mut ComponentRepresentation, signal_name: &str, access: &[SliceCapacity], slice_route: &[SliceCapacity], - tags: TagInfo, + tags: &TagInfo, ) -> Result<(), MemoryError> { if !component.is_initialized{ ComponentRepresentation::assign_value_to_signal_no_init( @@ -234,36 +371,284 @@ impl ComponentRepresentation { } } - /* - Tags: - - If an input receives a value that does not contain a expected tag ==> error - - If an input receives a tag whose value is different to the expected (the one received earlier) ==> error - - */ - pub fn assign_value_to_signal_no_init( component: &mut ComponentRepresentation, signal_name: &str, access: &[SliceCapacity], slice_route: &[SliceCapacity], + tags: &TagInfo, + ) -> Result<(), MemoryError> { + + // check that the tags are correct and update values + ComponentRepresentation::handle_tag_assignment_no_init(component, signal_name, tags)?; + component.to_assign_inputs.push((signal_name.to_string(), access.to_vec(), slice_route.to_vec())); + + Result::Ok(()) + } + + pub fn assign_value_to_signal_init( + self: &mut ComponentRepresentation, + signal_name: &str, + access: &[SliceCapacity], + slice_route: &[SliceCapacity], + tags: &TagInfo, + ) -> Result<(), MemoryError> { + + if !self.is_preinitialized() { + return Result::Err(MemoryError::AssignmentError(TypeAssignmentError::NoInitializedComponent)); + } + + if !self.inputs.contains_key(signal_name){ + return Result::Err(MemoryError::AssignmentError(TypeAssignmentError::AssignmentOutput)); + } + + // Check that the assignment satisfies the tags requisites + ComponentRepresentation::handle_tag_assignment_init(self, signal_name, tags)?; + + + // Perform the assignment + let inputs_response = self.inputs.get_mut(signal_name).unwrap(); + perform_signal_assignment(inputs_response, &access, slice_route)?; + + // Update the value of unnasigned fields + ComponentRepresentation::update_unassigned_inputs(self, signal_name, slice_route); + + Result::Ok(()) + + } + + // Assign buses: Operations to assign buses -> case init and no init + + pub fn assign_value_to_bus( + component: &mut ComponentRepresentation, + bus_name: &str, + access: &[SliceCapacity], + bus_slice: BusSlice, + tags: &TagInfo, + ) -> Result<(), MemoryError> { + if !component.is_initialized{ + ComponentRepresentation::assign_value_to_bus_no_init( + component, + bus_name, + access, + bus_slice, + tags + ) + } else { + ComponentRepresentation::assign_value_to_bus_init( + component, + bus_name, + access, + &bus_slice, + tags + ) + } + } + + pub fn assign_value_to_bus_no_init( + component: &mut ComponentRepresentation, + bus_name: &str, + access: &[SliceCapacity], + bus_slice: BusSlice, + tags: &TagInfo, + ) -> Result<(), MemoryError> { + + // check that the tags are correct and update values + ComponentRepresentation::handle_tag_assignment_no_init(component, bus_name, tags)?; + component.to_assign_input_buses.push((bus_name.to_string(), access.to_vec(), bus_slice)); + + Result::Ok(()) + } + + pub fn assign_value_to_bus_init( + self: &mut ComponentRepresentation, + bus_name: &str, + access: &[SliceCapacity], + bus_slice: &BusSlice, + tags: &TagInfo, + ) -> Result<(), MemoryError> { + + if !self.is_preinitialized() { + return Result::Err(MemoryError::AssignmentError(TypeAssignmentError::NoInitializedComponent)); + } + + if !self.input_buses.contains_key(bus_name){ + return Result::Err(MemoryError::AssignmentError(TypeAssignmentError::AssignmentOutput)); + } + + // Check that the assignment satisfies the tags requisites + ComponentRepresentation::handle_tag_assignment_init(self, bus_name, tags)?; + + // Perform the assignment + let inputs_response = self.input_buses.get_mut(bus_name).unwrap(); + perform_bus_assignment(inputs_response, &access, bus_slice, true)?; + + // Update the value of unnasigned fields + ComponentRepresentation::update_unassigned_inputs(self, bus_name, bus_slice.route()); + + Result::Ok(()) + + } + + + + // Assign bus field: Operations to assign bus fields -> case init and no init + + pub fn assign_value_to_bus_field( + component: &mut ComponentRepresentation, + bus_name: &str, + access: &AccessingInformationBus, + field_value: FoldedResult, + tags: &TagInfo, + ) -> Result<(), MemoryError> { + if !component.is_initialized{ + ComponentRepresentation::assign_value_to_bus_field_no_init( + component, + bus_name, + access, + field_value, + tags + ) + } else { + ComponentRepresentation::assign_value_to_bus_field_init( + component, + bus_name, + access, + &field_value, + tags.clone() + ) + } + } + + pub fn assign_value_to_bus_field_no_init( + component: &mut ComponentRepresentation, + bus_name: &str, + access: &AccessingInformationBus, + field_value: FoldedResult, + tags: &TagInfo, + ) -> Result<(), MemoryError> { + + // check that the tags are correct and update values, in this case none inputs + // are assigned to the complete bus + ComponentRepresentation::handle_tag_assignment_no_init( + component, + bus_name, + &TagInfo::new())?; + + component.to_assign_input_bus_fields.push(( + bus_name.to_string(), + access.clone(), + field_value, + tags.clone() + ) + ); + + Result::Ok(()) + } + + pub fn assign_value_to_bus_field_init( + self: &mut ComponentRepresentation, + bus_name: &str, + access: &AccessingInformationBus, + field_value: &FoldedResult, tags: TagInfo, ) -> Result<(), MemoryError> { - // We copy tags in any case, complete or incomplete assignment - // The values of the tags must be the same than the ones stored before - if !component.is_preinitialized() { + if !self.is_preinitialized() { return Result::Err(MemoryError::AssignmentError(TypeAssignmentError::NoInitializedComponent)); } + if !self.input_buses.contains_key(bus_name){ + return Result::Err(MemoryError::AssignmentError(TypeAssignmentError::AssignmentOutput)); + } + + + // Get the assigned input bus + let inputs_slice = self.input_buses.get_mut(bus_name).unwrap(); + + let access_result = BusSlice::access_values_by_mut_reference(inputs_slice, &access.array_access); + let mut accessed_bus = match access_result{ + Ok(value) => value, + Err(err) => return Err(err) + }; + + assert!(accessed_bus.len() == 1); + let single_bus = accessed_bus.get_mut(0).unwrap(); + assert!(access.field_access.is_some()); + + // call to bus representation to perform the assignment + let route_signal; + + let folded_arg = match field_value{ + FoldedResult::Signal (ss)=>{ + route_signal = ss.route().to_vec(); + FoldedArgument::Signal(&route_signal) + }, + FoldedResult::Bus (bs)=>{ + FoldedArgument::Bus(&bs) + }, + FoldedResult::Tag(_) =>{ + unreachable!() + } + }; + + single_bus.assign_value_to_field( + access.field_access.as_ref().unwrap(), + access.remaining_access.as_ref().unwrap(), + folded_arg, + Some(tags), + true, // it is an input so check tags instead of propagate + )?; + + + // In case it is completely assigned update unassigned + + if !single_bus.has_unassigned_fields(){ + ComponentRepresentation::update_unassigned_inputs(self, bus_name, &[1]); + } + + Result::Ok(()) + + } + + + pub fn is_preinitialized(&self) -> bool { + self.node_pointer.is_some() + } + + pub fn is_ready_initialize(&self) -> bool { + self.unassigned_tags.is_empty() + } + + pub fn has_unassigned_inputs(&self) -> bool{ + !self.unassigned_inputs.is_empty() + } + + + /* + Tags: + - If an input receives a value that does not contain a expected tag ==> error + - If an input receives a tag whose value is different to the expected (the one received earlier) ==> error + + */ + + + fn handle_tag_assignment_no_init(component: &mut ComponentRepresentation, signal_name: &str, tags: &TagInfo) -> Result<(), MemoryError> { + + if !component.is_preinitialized() { + return Result::Err(MemoryError::AssignmentError(TypeAssignmentError::NoInitializedComponent)); + } if !component.inputs_tags.contains_key(signal_name){ return Result::Err(MemoryError::AssignmentError(TypeAssignmentError::AssignmentOutput)); } let tags_input = component.inputs_tags.get_mut(signal_name).unwrap(); - let is_first_assignment_signal = component.unassigned_tags.contains(signal_name); component.unassigned_tags.remove(signal_name); + // We copy tags in any case, complete or incomplete assignment + // The values of the tags must be the same than the ones stored before + for (t, value) in tags_input{ if !tags.contains_key(t){ return Result::Err(MemoryError::AssignmentMissingTags(signal_name.to_string(), t.clone())); @@ -279,32 +664,16 @@ impl ComponentRepresentation { } } } - component.to_assign_inputs.push((signal_name.to_string(), access.to_vec(), slice_route.to_vec())); Result::Ok(()) } - pub fn assign_value_to_signal_init( - component: &mut ComponentRepresentation, - signal_name: &str, - access: &[SliceCapacity], - slice_route: &[SliceCapacity], - tags: TagInfo, - ) -> Result<(), MemoryError> { - - if !component.is_preinitialized() { - return Result::Err(MemoryError::AssignmentError(TypeAssignmentError::NoInitializedComponent)); - } - - if !component.inputs.contains_key(signal_name){ - return Result::Err(MemoryError::AssignmentError(TypeAssignmentError::AssignmentOutput)); - } - - let tags_input = component.inputs_tags.get_mut(signal_name).unwrap(); + fn handle_tag_assignment_init(component: &ComponentRepresentation, signal_name: &str, tags: &TagInfo)-> Result<(), MemoryError>{ + let tags_input = component.inputs_tags.get(signal_name).unwrap(); for (t, value) in tags_input{ if !tags.contains_key(t){ return Result::Err(MemoryError::AssignmentMissingTags(signal_name.to_string(), t.clone())); } else{ - // We are in the case where the component is initialized, so we + // We are in the case wher.e the component is initialized, so we // assume that all tags already have their value and check if it is // the same as the one we are receiving if value != tags.get(t).unwrap(){ @@ -312,59 +681,25 @@ impl ComponentRepresentation { } } } + Ok(()) + } - let inputs_response = component.inputs.get_mut(signal_name).unwrap(); - let signal_previous_value = SignalSlice::access_values( - inputs_response, - &access, - )?; - - let new_value_slice = &SignalSlice::new_with_route(slice_route, &true); - - SignalSlice::check_correct_dims( - &signal_previous_value, - &Vec::new(), - &new_value_slice, - true - )?; + // Auxiliar function to update the unassigned inputs - for i in 0..SignalSlice::get_number_of_cells(&signal_previous_value){ - let signal_was_assigned = SignalSlice::access_value_by_index(&signal_previous_value, i)?; - if signal_was_assigned { - return Result::Err(MemoryError::AssignmentError(TypeAssignmentError::MultipleAssignments)); - } + fn update_unassigned_inputs(component: &mut ComponentRepresentation, signal_name: &str, slice_route: &[usize]){ + let mut dim_slice = 1; + for i in slice_route { + dim_slice *= *i; } - - SignalSlice::insert_values( - inputs_response, - &access, - &new_value_slice, - true - )?; - let dim = SignalSlice::get_number_of_cells(new_value_slice); match component.unassigned_inputs.get_mut(signal_name){ Some(left) => { - *left -= dim; + *left -= dim_slice; if *left == 0 { component.unassigned_inputs.remove(signal_name); } } - None => {} + Option::None => {} } - - Result::Ok(()) - - } - pub fn is_preinitialized(&self) -> bool { - self.node_pointer.is_some() } - - pub fn is_ready_initialize(&self) -> bool { - self.unassigned_tags.is_empty() - } - - pub fn has_unassigned_inputs(&self) -> bool{ - !self.unassigned_inputs.is_empty() - } - } + diff --git a/constraint_generation/src/environment_utils/environment.rs b/constraint_generation/src/environment_utils/environment.rs index 3dca8c5a8..f1be900e7 100644 --- a/constraint_generation/src/environment_utils/environment.rs +++ b/constraint_generation/src/environment_utils/environment.rs @@ -1,19 +1,21 @@ use super::slice_types::{ AExpressionSlice, ComponentRepresentation, + BusRepresentation, ComponentSlice, SignalSlice, SliceCapacity, TagInfo, TagDefinitions, - TagState + TagState, + BusSlice }; use super::{ArithmeticExpression, CircomEnvironment, CircomEnvironmentError}; use program_structure::memory_slice::MemoryError; use crate::ast::Meta; pub type ExecutionEnvironmentError = CircomEnvironmentError; -pub type ExecutionEnvironment = CircomEnvironment; +pub type ExecutionEnvironment = CircomEnvironment; pub fn environment_shortcut_add_component( environment: &mut ExecutionEnvironment, @@ -23,6 +25,7 @@ pub fn environment_shortcut_add_component( let slice = ComponentSlice::new_with_route(dimensions, &ComponentRepresentation::default()); environment.add_component(component_name, slice); } + pub fn environment_shortcut_add_input( environment: &mut ExecutionEnvironment, input_name: &str, @@ -63,6 +66,47 @@ pub fn environment_shortcut_add_intermediate( } environment.add_intermediate(intermediate_name, (tags.clone(), tags_defined, slice)); } +pub fn environment_shortcut_add_bus_input( + environment: &mut ExecutionEnvironment, + input_name: &str, + dimensions: &[SliceCapacity], + tags: &TagInfo, +) { + // In this case we need to set all the signals of the bus to known -> in the default method + let slice = BusSlice::new_with_route(dimensions, &BusRepresentation::default()); + let mut tags_defined = TagDefinitions::new(); + for (t, value) in tags{ + tags_defined.insert(t.clone(), TagState{defined:true, value_defined: value.is_some(), complete: true}); + } + + environment.add_input_bus(input_name, (tags.clone(), tags_defined, slice)); +} +pub fn environment_shortcut_add_bus_output( + environment: &mut ExecutionEnvironment, + output_name: &str, + dimensions: &[SliceCapacity], + tags: &TagInfo, +) { + let slice = BusSlice::new_with_route(dimensions, &BusRepresentation::default()); + let mut tags_defined = TagDefinitions::new(); + for (t, value) in tags{ + tags_defined.insert(t.clone(), TagState{defined:true, value_defined: value.is_some(), complete: false}); + } + environment.add_output_bus(output_name, (tags.clone(), tags_defined, slice)); +} +pub fn environment_shortcut_add_bus_intermediate( + environment: &mut ExecutionEnvironment, + intermediate_name: &str, + dimensions: &[SliceCapacity], + tags: &TagInfo, +) { + let slice = BusSlice::new_with_route(dimensions, &BusRepresentation::default()); + let mut tags_defined = TagDefinitions::new(); + for (t, value) in tags{ + tags_defined.insert(t.clone(), TagState{defined:true, value_defined: value.is_some(), complete: false}); + } + environment.add_intermediate_bus(intermediate_name, (tags.clone(), tags_defined, slice)); +} pub fn environment_shortcut_add_variable( environment: &mut ExecutionEnvironment, variable_name: &str, diff --git a/constraint_generation/src/environment_utils/mod.rs b/constraint_generation/src/environment_utils/mod.rs index 4cb388acd..0e9f36cad 100644 --- a/constraint_generation/src/environment_utils/mod.rs +++ b/constraint_generation/src/environment_utils/mod.rs @@ -2,6 +2,7 @@ use super::ArithmeticExpression; use program_structure::environment::{CircomEnvironment, CircomEnvironmentError}; use program_structure::utils::memory_slice; +mod bus_representation; mod component_representation; pub mod environment; pub mod slice_types; diff --git a/constraint_generation/src/environment_utils/slice_types.rs b/constraint_generation/src/environment_utils/slice_types.rs index 83e99fc4b..e5426fe99 100644 --- a/constraint_generation/src/environment_utils/slice_types.rs +++ b/constraint_generation/src/environment_utils/slice_types.rs @@ -1,4 +1,5 @@ pub use super::component_representation::ComponentRepresentation; +pub use super::bus_representation::BusRepresentation; pub use super::memory_slice::MemorySlice; pub use super::memory_slice::{MemoryError, TypeInvalidAccess, TypeAssignmentError, SliceCapacity}; pub use circom_algebra::algebra::ArithmeticExpression; @@ -17,3 +18,30 @@ pub type AExpressionSlice = MemorySlice>; // The boolean is true if the signal contains a value pub type SignalSlice = MemorySlice; pub type ComponentSlice = MemorySlice; + +// To store the buses, similar to the components +pub type BusSlice = MemorySlice; + +// To store the fields of a bus +#[derive(Clone)] +pub enum FieldTypes { // For each field, we store the info depending on if it is a signal o a bus + // Depending on the case we store a different slice + Signal(SignalSlice), + Bus(BusSlice), +} + +#[derive(Clone)] +pub enum FoldedResult { // For each possible returning value, we store the info depending on if it is a signal o a bus + // Depending on the case we store a different slice + Signal(SignalSlice), + Bus(BusSlice), + Tag(BigInt) +} + + +pub enum FoldedArgument<'a> { // For each possible argument, we store the info depending on if it is a signal o a bus + // Depending on the case we store a different slice + Signal(&'a Vec), + Bus(&'a BusSlice), + Tag(&'a BigInt) +} \ No newline at end of file diff --git a/constraint_generation/src/execute.rs b/constraint_generation/src/execute.rs index 887d52c38..f48f26a47 100644 --- a/constraint_generation/src/execute.rs +++ b/constraint_generation/src/execute.rs @@ -2,26 +2,33 @@ use super::environment_utils::{ environment::{ environment_shortcut_add_component, environment_shortcut_add_input, environment_shortcut_add_intermediate, environment_shortcut_add_output, + environment_shortcut_add_bus_input, environment_shortcut_add_bus_intermediate, + environment_shortcut_add_bus_output, environment_shortcut_add_variable, ExecutionEnvironment, ExecutionEnvironmentError, environment_check_all_components_assigned }, slice_types::{ AExpressionSlice, ArithmeticExpression as ArithmeticExpressionGen, ComponentRepresentation, ComponentSlice, MemoryError, TypeInvalidAccess, TypeAssignmentError, MemorySlice, - SignalSlice, SliceCapacity, TagInfo, TagState + SignalSlice, SliceCapacity, TagInfo, BusSlice, BusRepresentation, + FoldedResult, FoldedArgument }, }; +use crate::assignment_utils::*; + + use program_structure::constants::UsefulConstants; use super::execution_data::analysis::Analysis; -use super::execution_data::{ExecutedProgram, ExecutedTemplate, PreExecutedTemplate, NodePointer}; +use super::execution_data::{ExecutedBus, ExecutedProgram, ExecutedTemplate, PreExecutedTemplate, NodePointer}; +use super::execution_data::type_definitions::{AccessingInformationBus, AccessingInformation}; + use super::{ ast::*, ArithmeticError, FileID, ProgramArchive, Report, ReportCode, ReportCollection }; use circom_algebra::num_bigint::BigInt; use std::collections::{HashMap, BTreeMap}; -use std::mem; use crate::FlagsExecution; type AExpr = ArithmeticExpressionGen; type AnonymousComponentsInfo = BTreeMap)>; @@ -63,16 +70,32 @@ impl RuntimeInformation { struct FoldedValue { pub arithmetic_slice: Option, + pub bus_slice: Option<(String, BusSlice)>, // stores the name of the bus and the value pub node_pointer: Option, + pub bus_node_pointer: Option, pub is_parallel: Option, pub tags: Option, } impl FoldedValue { pub fn valid_arithmetic_slice(f_value: &FoldedValue) -> bool { - f_value.arithmetic_slice.is_some() && f_value.node_pointer.is_none() && f_value.is_parallel.is_none() + f_value.arithmetic_slice.is_some() && f_value.node_pointer.is_none() && + f_value.is_parallel.is_none() && f_value.bus_node_pointer.is_none() + && f_value.bus_slice.is_none() + } + pub fn valid_bus_slice(f_value: &FoldedValue) -> bool { + f_value.bus_slice.is_some() && f_value.node_pointer.is_none() && + f_value.is_parallel.is_none() && f_value.bus_node_pointer.is_none() + && f_value.arithmetic_slice.is_none() } pub fn valid_node_pointer(f_value: &FoldedValue) -> bool { - f_value.node_pointer.is_some() && f_value.is_parallel.is_some() && f_value.arithmetic_slice.is_none() + f_value.node_pointer.is_some() && f_value.is_parallel.is_some() && + f_value.arithmetic_slice.is_none() && f_value.bus_node_pointer.is_none() + && f_value.bus_slice.is_none() + } + pub fn valid_bus_node_pointer(f_value: &FoldedValue) -> bool{ + f_value.bus_node_pointer.is_some() && f_value.node_pointer.is_none() && + f_value.is_parallel.is_none() && f_value.arithmetic_slice.is_none() + && f_value.bus_slice.is_none() } } @@ -80,7 +103,9 @@ impl Default for FoldedValue { fn default() -> Self { FoldedValue { arithmetic_slice: Option::None, + bus_slice: Option::None, node_pointer: Option::None, + bus_node_pointer: Option::None, is_parallel: Option::None, tags: Option::None, } @@ -269,8 +294,7 @@ fn execute_statement( name, &usable_dimensions, ), - VariableType::Signal(signal_type, tag_list) => - { + VariableType::Signal(signal_type, tag_list) => { if runtime.block_type == BlockType::Unknown{ // Case not valid constraint Known/Unknown let err = Result::Err(ExecutionError::DeclarationInUnknown); @@ -290,6 +314,26 @@ fn execute_statement( actual_node, ) }, + VariableType::Bus(_id, signal_type, tag_list) => { + if runtime.block_type == BlockType::Unknown{ + // Case not valid constraint Known/Unknown + let err = Result::Err(ExecutionError::DeclarationInUnknown); + treat_result_with_execution_error( + err, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + } + execute_bus_declaration( + name, + &usable_dimensions, + tag_list, + *signal_type, + &mut runtime.environment, + actual_node, + ) + }, _ =>{ unreachable!() } @@ -300,10 +344,40 @@ fn execute_statement( Option::None } Substitution { meta, var, access, op, rhe, .. } => { - let access_information = treat_accessing(meta, access, program_archive, runtime, flags)?; + let access_information = + if ExecutionEnvironment::has_bus(&runtime.environment, var) || ExecutionEnvironment::has_component(&runtime.environment, var){ + let access_bus = treat_accessing_bus(meta, access, program_archive, runtime, flags)?; + TypesAccess{bus_access: Some(access_bus), other_access: None} + } else{ + let access_other = treat_accessing(meta, access, program_archive, runtime, flags)?; + TypesAccess{bus_access: None, other_access: Some(access_other)} + }; + + + let r_folded = execute_expression(rhe, program_archive, runtime, flags)?; + + let mut struct_node = if actual_node.is_some(){ + ExecutedStructure::Template(actual_node.as_mut().unwrap()) + } else{ + ExecutedStructure::None + }; + + let possible_constraint = - perform_assign(meta, var, *op, &access_information, r_folded, actual_node, runtime, program_archive, flags)?; + perform_assign( + meta, + var, + *op, + &access_information, + r_folded, + &mut struct_node, + runtime, + program_archive, + flags + )?; + + if let Option::Some(node) = actual_node { if *op == AssignOp::AssignConstraintSignal || (*op == AssignOp::AssignSignal && flags.inspect){ debug_assert!(possible_constraint.is_some()); @@ -323,6 +397,7 @@ fn execute_statement( let constrained = possible_constraint.unwrap(); let mut needs_double_arrow = Vec::new(); + for i in 0..AExpressionSlice::get_number_of_cells(&constrained.right){ let value_right = treat_result_with_memory_error( AExpressionSlice::access_value_by_index(&constrained.right, i), @@ -332,17 +407,13 @@ fn execute_statement( )?; - let access_left = treat_result_with_memory_error( - AExpressionSlice::get_access_index(&constrained.right, i), + let signal_left = treat_result_with_memory_error( + AExpressionSlice::access_value_by_index(&constrained.left, i), meta, &mut runtime.runtime_errors, &runtime.call_trace, )?; - - let full_symbol = format!("{}{}", - constrained.left, - create_index_appendix(&access_left), - ); + if let AssignOp::AssignConstraintSignal = op { if value_right.is_nonquadratic() { let err = Result::Err(ExecutionError::NonQuadraticConstraint); @@ -354,15 +425,22 @@ fn execute_statement( )?; } else { let p = runtime.constants.get_p().clone(); - let symbol = AExpr::Signal { symbol: full_symbol }; + let symbol = signal_left; let expr = AExpr::sub(&symbol, &value_right, &p); let ctr = AExpr::transform_expression_to_constraint_form(expr, &p).unwrap(); node.add_constraint(ctr); } } else if let AssignOp::AssignSignal = op {// needs fix, check case arrays //debug_assert!(possible_constraint.is_some()); + let signal_name = match signal_left{ + AExpr::Signal { symbol } =>{ + symbol + }, + _ => unreachable!() + }; + if !value_right.is_nonquadratic() && !node.is_custom_gate { - needs_double_arrow.push(full_symbol); + needs_double_arrow.push(signal_name); } } } @@ -411,31 +489,112 @@ fn execute_statement( let f_left = execute_expression(lhe, program_archive, runtime, flags)?; let f_right = execute_expression(rhe, program_archive, runtime, flags)?; - let arith_left = safe_unwrap_to_arithmetic_slice(f_left, line!()); - let arith_right = safe_unwrap_to_arithmetic_slice(f_right, line!()); - - - let correct_dims_result = AExpressionSlice::check_correct_dims(&arith_left, &Vec::new(), &arith_right, true); - treat_result_with_memory_error_void( - correct_dims_result, - meta, - &mut runtime.runtime_errors, - &runtime.call_trace, - )?; - for i in 0..AExpressionSlice::get_number_of_cells(&arith_left){ - let value_left = treat_result_with_memory_error( - AExpressionSlice::access_value_by_index(&arith_left, i), + let (arith_left, arith_right) = if FoldedValue::valid_arithmetic_slice(&f_left) && FoldedValue::valid_arithmetic_slice(&f_right){ + let left = safe_unwrap_to_arithmetic_slice(f_left, line!()); + let right = safe_unwrap_to_arithmetic_slice(f_right, line!()); + let correct_dims_result = AExpressionSlice::check_correct_dims(&left, &Vec::new(), &right, true); + treat_result_with_memory_error_void( + correct_dims_result, meta, &mut runtime.runtime_errors, &runtime.call_trace, )?; - let value_right = treat_result_with_memory_error( - AExpressionSlice::access_value_by_index(&arith_right, i), + (left.destruct().1, right.destruct().1) + } else if FoldedValue::valid_bus_slice(&f_left) && FoldedValue::valid_bus_slice(&f_right){ + let (name_left, slice_left) = safe_unwrap_to_bus_slice(f_left, line!()); + let (name_right, slice_right) = safe_unwrap_to_bus_slice(f_right, line!()); + + // Generate an arithmetic slice for the buses left and right + let mut signals_values_right: Vec = Vec::new(); + let mut signals_values_left: Vec = Vec::new(); + + // Check that the dimensions of the slices are equal + let correct_dims_result = BusSlice::check_correct_dims(&slice_left, &Vec::new(), &slice_right, true); + treat_result_with_memory_error_void( + correct_dims_result, meta, &mut runtime.runtime_errors, &runtime.call_trace, )?; + + // Check that the types of the buses are equal + // and get the accesses inside the bus + // We assume that the buses in the slice are all of the same type + // Generate the arithmetic slices containing the signals + // Use just the first to generate the bus accesses + + let mut inside_bus_signals = Vec::new(); + + if BusSlice::get_number_of_cells(&slice_left) > 0{ + let left_i = treat_result_with_memory_error( + BusSlice::get_reference_to_single_value_by_index(&slice_left, 0), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + let right_i = treat_result_with_memory_error( + BusSlice::get_reference_to_single_value_by_index(&slice_right, 0), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + // ensure same type of bus + if left_i.node_pointer != right_i.node_pointer{ + treat_result_with_memory_error( + Result::Err(MemoryError::MismatchedInstances), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + } + // generate the inside signals + inside_bus_signals = left_i.get_accesses_bus(""); + + } + + for i in 0..BusSlice::get_number_of_cells(&slice_left){ + + let access_index = treat_result_with_memory_error( + BusSlice::get_access_index(&slice_left, i), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + let string_index = create_index_appendix(&access_index); + + for s in &inside_bus_signals{ + signals_values_right.push( + format!( + "{}{}{}", name_right, string_index, s.clone() + )); + signals_values_left.push( + format!( + "{}{}{}", name_left, string_index, s.clone() + )); + } + + } + + // Transform the signal names into Arithmetic Expressions + let mut ae_signals_left = Vec::new(); + for signal_name in signals_values_left{ + ae_signals_left.push(AExpr::Signal { symbol: signal_name }); + } + + let mut ae_signals_right = Vec::new(); + for signal_name in signals_values_right{ + ae_signals_right.push(AExpr::Signal { symbol: signal_name }); + } + + (ae_signals_left, ae_signals_right) + } else{ + unreachable!() + }; + + for i in 0..arith_left.len(){ + let value_left = &arith_left[i]; + let value_right = &arith_right[i]; let possible_non_quadratic = AExpr::sub( &value_left, @@ -577,25 +736,76 @@ fn execute_statement( } UnderscoreSubstitution{ meta, rhe, op} =>{ let f_result = execute_expression(rhe, program_archive, runtime, flags)?; - let arithmetic_slice = safe_unwrap_to_arithmetic_slice(f_result, line!()); - if *op == AssignOp::AssignConstraintSignal{ - for i in 0..AExpressionSlice::get_number_of_cells(&arithmetic_slice){ - let value_cell = treat_result_with_memory_error( - AExpressionSlice::access_value_by_index(&arithmetic_slice, i), + if FoldedValue::valid_arithmetic_slice(&f_result){ + let arithmetic_slice = safe_unwrap_to_arithmetic_slice(f_result, line!()); + if *op == AssignOp::AssignConstraintSignal{ + for i in 0..AExpressionSlice::get_number_of_cells(&arithmetic_slice){ + let value_cell = treat_result_with_memory_error( + AExpressionSlice::access_value_by_index(&arithmetic_slice, i), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + let constraint_expression = AExpr::transform_expression_to_constraint_form( + value_cell, + runtime.constants.get_p(), + ).unwrap(); + if let Option::Some(node) = actual_node { + for signal in constraint_expression.take_signals(){ + node.add_underscored_signal(signal); + } + } + } + } + } else if FoldedValue::valid_bus_slice(&f_result){ + let (bus_name, bus_slice) = safe_unwrap_to_bus_slice(f_result, line!()); + let mut signal_values = Vec::new(); + + // Get the accesses inside the bus + // We assume that the buses in the slice are all of the same type + // Generate the arithmetic slices containing the signals + // Use just the first to generate the bus accesses + + let mut inside_bus_signals = Vec::new(); + + if BusSlice::get_number_of_cells(&bus_slice) > 0{ + let left_i = treat_result_with_memory_error( + BusSlice::get_reference_to_single_value_by_index(&bus_slice, 0), meta, &mut runtime.runtime_errors, &runtime.call_trace, )?; - let constraint_expression = AExpr::transform_expression_to_constraint_form( - value_cell, - runtime.constants.get_p(), - ).unwrap(); - if let Option::Some(node) = actual_node { - for signal in constraint_expression.take_signals(){ - node.add_underscored_signal(signal); - } + // generate the inside signals + inside_bus_signals = left_i.get_accesses_bus(""); + } + + for i in 0..BusSlice::get_number_of_cells(&bus_slice){ + + let access_index = treat_result_with_memory_error( + BusSlice::get_access_index(&bus_slice, i), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + let string_index = create_index_appendix(&access_index); + + for s in &inside_bus_signals{ + signal_values.push( + format!( + "{}{}{}", bus_name, string_index, s.clone() + )); } + + } + + if let Option::Some(node) = actual_node { + for signal_name in &signal_values{ + node.add_underscored_signal(signal_name); + } } + + } else{ + unreachable!() } Option::None } @@ -603,6 +813,98 @@ fn execute_statement( Result::Ok((res, can_be_simplified)) } +fn execute_bus_statement( + stmt: &Statement, + program_archive: &ProgramArchive, + runtime: &mut RuntimeInformation, + actual_node: &mut ExecutedBus, + flags: FlagsExecution +)-> Result<(), ()>{ + use Statement::*; + let id = stmt.get_meta().elem_id; + Analysis::reached(&mut runtime.analysis, id); + let _res = match stmt { + InitializationBlock { initializations, .. } => { + execute_sequence_of_bus_statements( + initializations, + program_archive, + runtime, + actual_node, + flags, + )? + } + Declaration { meta, xtype, name, dimensions, .. } => { + + let mut arithmetic_values = Vec::new(); + for dimension in dimensions.iter() { + let f_dimensions = + execute_expression(dimension, program_archive, runtime, flags)?; + arithmetic_values + .push(safe_unwrap_to_single_arithmetic_expression(f_dimensions, line!())); + } + treat_result_with_memory_error_void( + valid_array_declaration(&arithmetic_values), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + let usable_dimensions = + if let Option::Some(dimensions) = cast_indexing(&arithmetic_values) { + dimensions + } else { + let err = Result::Err(ExecutionError::ArraySizeTooBig); + treat_result_with_execution_error( + err, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )? + }; + match xtype { + + VariableType::Signal(_signal_type, tag_list) => + execute_declaration_bus(name, &usable_dimensions, tag_list, &mut runtime.environment, actual_node, false), + + VariableType::Bus(_id, _signal_type, tag_list) => + execute_declaration_bus(name, &usable_dimensions, tag_list, &mut runtime.environment, actual_node, true), + + _ =>{ + unreachable!() + } + } + } + Substitution { meta, var, access, op, rhe, .. } => { + // different access information depending if bus or other variable + let access_information = + if ExecutionEnvironment::has_bus(&runtime.environment, var) || ExecutionEnvironment::has_component(&runtime.environment, var){ + let access_bus = treat_accessing_bus(meta, access, program_archive, runtime, flags)?; + TypesAccess{bus_access: Some(access_bus), other_access: None} + } else{ + let access_other = treat_accessing(meta, access, program_archive, runtime, flags)?; + TypesAccess{bus_access: None, other_access: Some(access_other)} + }; + let r_folded = execute_expression(rhe, program_archive, runtime, flags)?; + let _possible_constraint = + perform_assign( + meta, + var, + *op, + &access_information, + r_folded, + &mut ExecutedStructure::Bus(actual_node), + runtime, + program_archive, + flags + )?; + } + Block { stmts, .. } => { + execute_sequence_of_bus_statements(stmts, program_archive, runtime, actual_node, flags)?; + } + _ => unreachable!(), + }; + Result::Ok(()) +} + fn execute_expression( expr: &Expression, program_archive: &ProgramArchive, @@ -624,7 +926,10 @@ fn execute_expression( execute_component(meta, name, access, program_archive, runtime, flags)? } else if ExecutionEnvironment::has_variable(&runtime.environment, name) { execute_variable(meta, name, access, program_archive, runtime, flags)? - } else { + } else if ExecutionEnvironment::has_bus(&runtime.environment, name){ + execute_bus(meta, name, access, program_archive, runtime, flags)? + } + else { unreachable!(); } } @@ -670,31 +975,40 @@ fn execute_expression( }; let f_value = execute_expression(value, program_archive, runtime, flags)?; - let slice_value = safe_unwrap_to_arithmetic_slice(f_value, line!()); + if FoldedValue::valid_arithmetic_slice(&f_value){ + let slice_value = safe_unwrap_to_arithmetic_slice(f_value, line!()); - let mut dims = vec![usable_dimension]; - for dim in slice_value.route() { - dims.push(*dim); - } - - let mut array_slice = AExpressionSlice::new_with_route(&dims, &AExpr::default()); - let mut row: SliceCapacity = 0; - while row < usable_dimension { - let memory_insert_result = AExpressionSlice::insert_values( - &mut array_slice, - &[row], - &slice_value, - false - ); - treat_result_with_memory_error_void( - memory_insert_result, - meta, - &mut runtime.runtime_errors, - &runtime.call_trace, - )?; - row += 1; + let mut dims = vec![usable_dimension]; + for dim in slice_value.route() { + dims.push(*dim); + } + + let mut array_slice = AExpressionSlice::new_with_route(&dims, &AExpr::default()); + let mut row: SliceCapacity = 0; + while row < usable_dimension { + let memory_insert_result = AExpressionSlice::insert_values( + &mut array_slice, + &[row], + &slice_value, + false + ); + treat_result_with_memory_error_void( + memory_insert_result, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + row += 1; + } + FoldedValue { arithmetic_slice: Option::Some(array_slice), ..FoldedValue::default() } + } else if FoldedValue::valid_bus_node_pointer(&f_value){ + let node_pointer = safe_unwrap_to_valid_bus_node_pointer(f_value, line!()); + + FoldedValue { bus_node_pointer: Option::Some(node_pointer), ..FoldedValue::default() } + } else{ + unreachable!(); } - FoldedValue { arithmetic_slice: Option::Some(array_slice), ..FoldedValue::default() } + } InfixOp { meta, lhe, infix_op, rhe, .. } => { let l_fold = execute_expression(lhe, program_archive, runtime, flags)?; @@ -734,6 +1048,11 @@ fn execute_expression( can_be_simplified = can_simplify; value } + BusCall{id, args, ..} =>{ + let value = execute_bus_call_complete(id, args, program_archive, runtime, flags)?; + value + + } ParallelOp{rhe, ..} => { let folded_value = execute_expression(rhe, program_archive, runtime, flags)?; let (node_pointer, _) = @@ -865,6 +1184,43 @@ fn execute_anonymous_component_declaration( anonymous_components.insert(component_name.to_string(), (meta, dimensions.clone())); } +fn execute_bus_call_complete( + id: &String, + args: &Vec, + program_archive: &ProgramArchive, + runtime: &mut RuntimeInformation, + flags: FlagsExecution, +) -> Result { + let mut arg_values = Vec::new(); + + for arg_expression in args.iter() { + let f_arg = execute_expression(arg_expression, program_archive, runtime, flags)?; + arg_values.push(safe_unwrap_to_arithmetic_slice(f_arg, line!())); + } + + if program_archive.contains_bus(id){ // in this case we execute + let new_environment = prepare_environment_for_call(id, &arg_values, program_archive); + let previous_environment = std::mem::replace(&mut runtime.environment, new_environment); + let previous_block_type = std::mem::replace(&mut runtime.block_type, BlockType::Known); + let previous_anonymous_components = std::mem::replace(&mut runtime.anonymous_components, AnonymousComponentsInfo::new()); + + let new_file_id = program_archive.get_bus_data(id).get_file_id(); + let previous_id = std::mem::replace(&mut runtime.current_file, new_file_id); + + runtime.call_trace.push(id.clone()); + let folded_result = execute_bus_call(id, arg_values, program_archive, runtime, flags)?; + + runtime.environment = previous_environment; + runtime.current_file = previous_id; + runtime.block_type = previous_block_type; + runtime.anonymous_components = previous_anonymous_components; + runtime.call_trace.pop(); + Ok(folded_result) + } else{ + unreachable!() + } +} + fn execute_signal_declaration( signal_name: &str, dimensions: &[SliceCapacity], @@ -879,7 +1235,6 @@ fn execute_signal_declaration( tags.insert(t.clone(), None); } if let Option::Some(node) = actual_node { - node.add_ordered_signal(signal_name, dimensions); match signal_type { Input => { if let Some(tags_input) = node.tag_instances().get(signal_name){ @@ -887,15 +1242,15 @@ fn execute_signal_declaration( } else{ environment_shortcut_add_input(environment, signal_name, dimensions, &tags); } - node.add_input(signal_name, dimensions); + node.add_input(signal_name, dimensions, false); } Output => { environment_shortcut_add_output(environment, signal_name, dimensions, &tags); - node.add_output(signal_name, dimensions); + node.add_output(signal_name, dimensions, false); } Intermediate => { environment_shortcut_add_intermediate(environment, signal_name, dimensions, &tags); - node.add_intermediate(signal_name, dimensions); + node.add_intermediate(signal_name, dimensions, false); } } } else { @@ -903,30 +1258,113 @@ fn execute_signal_declaration( } } -/* - In case the assignment could be a constraint generator the returned value is the constraint - that will be created -*/ -struct Constrained { - left: String, - right: AExpressionSlice, +fn execute_declaration_bus( + signal_name: &str, + dimensions: &[SliceCapacity], + list_tags: &Vec, + environment: &mut ExecutionEnvironment, + actual_node: &mut ExecutedBus, + is_bus: bool, +) { + let mut tags = TagInfo::new(); + for t in list_tags{ + tags.insert(t.clone(), None); + } + + if is_bus{ + actual_node.add_bus(signal_name, dimensions); + environment_shortcut_add_bus_intermediate(environment, signal_name, dimensions, &tags); + } else{ + actual_node.add_signal(signal_name, dimensions); + environment_shortcut_add_intermediate(environment, signal_name, dimensions, &tags); + + } + for t in list_tags{ + actual_node.add_tag_signal(signal_name, t, None); + } } -fn perform_assign( + +fn execute_bus_declaration( + bus_name: &str, + dimensions: &[SliceCapacity], + list_tags: &Vec, + signal_type: SignalType, + environment: &mut ExecutionEnvironment, + actual_node: &mut Option, +) { + use SignalType::*; + let mut tags = TagInfo::new(); + for t in list_tags{ + tags.insert(t.clone(), None); + } + if let Option::Some(node) = actual_node { + match signal_type { + Input => { + if let Some(tags_input) = node.tag_instances().get(bus_name){ + environment_shortcut_add_bus_input(environment, bus_name, dimensions, &tags_input); + } else{ + environment_shortcut_add_bus_input(environment, bus_name, dimensions, &tags); + } + node.add_input(bus_name, dimensions, true); + } + Output => { + environment_shortcut_add_bus_output(environment, bus_name, dimensions, &tags); + node.add_output(bus_name, dimensions, true); + } + Intermediate => { + environment_shortcut_add_bus_intermediate(environment, bus_name, dimensions, &tags); + node.add_intermediate(bus_name, dimensions, true); + } + } + } else { + unreachable!(); + } +} + +/* + In case the assigment could be a constraint generator the returned value is the constraint + that will be created +*/ +enum ExecutedStructure<'a>{ + Template(&'a mut ExecutedTemplate), + Bus(&'a mut ExecutedBus), + None +} + +struct Constrained { + left: AExpressionSlice, + right: AExpressionSlice, +} + +struct TypesAccess{ + bus_access: Option, + other_access: Option, +} + + +fn perform_assign( meta: &Meta, symbol: &str, op: AssignOp, - accessing_information: &AccessingInformation, + accessing_information: &TypesAccess, r_folded: FoldedValue, - actual_node: &mut Option, + actual_node: &mut ExecutedStructure, runtime: &mut RuntimeInformation, program_archive: &ProgramArchive, flags: FlagsExecution ) -> Result, ()> { - use super::execution_data::type_definitions::SubComponentData; - let full_symbol = create_symbol(symbol, &accessing_information); + use super::execution_data::type_definitions::{SubComponentData, BusData}; - let possible_arithmetic_slice = if ExecutionEnvironment::has_variable(&runtime.environment, symbol) + let full_symbol = if accessing_information.bus_access.is_some(){ + create_symbol_bus(symbol, &accessing_information.bus_access.as_ref().unwrap()) + + } else{ + create_symbol(symbol, &accessing_information.other_access.as_ref().unwrap()) + }; + + let possible_arithmetic_slices = if ExecutionEnvironment::has_variable(&runtime.environment, symbol) { + let accessing_information = accessing_information.other_access.as_ref().unwrap(); debug_assert!(accessing_information.signal_access.is_none()); debug_assert!(accessing_information.after_signal.is_empty()); let environment_result = ExecutionEnvironment::get_mut_variable_mut(&mut runtime.environment, symbol); @@ -980,8 +1418,10 @@ fn perform_assign( } } Option::None - } else if ExecutionEnvironment::has_signal(&runtime.environment, symbol) && - accessing_information.signal_access.is_some() { + } else if ExecutionEnvironment::has_signal(&runtime.environment, symbol){ + let accessing_information = accessing_information.other_access.as_ref().unwrap(); + if accessing_information.signal_access.is_some() { + // it is a tag if ExecutionEnvironment::has_input(&runtime.environment, symbol) { treat_result_with_memory_error( Result::Err(MemoryError::AssignmentTagInput), @@ -1037,10 +1477,16 @@ fn perform_assign( reference_to_tags.insert(tag.clone(), Option::Some(value.clone())); let tag_state = reference_to_tags_defined.get_mut(&tag).unwrap(); tag_state.value_defined = true; - if let Option::Some(node) = actual_node{ - node.add_tag_signal(symbol, &tag, Some(value)); - } else{ - unreachable!(); + match actual_node{ + ExecutedStructure::Template(node) =>{ + node.add_tag_signal(symbol, &tag, Some(value)); + }, + ExecutedStructure::Bus(node) =>{ + node.add_tag_signal(symbol, &tag, Some(value)); + }, + ExecutedStructure::None => { + unreachable!(); + } } } @@ -1060,7 +1506,8 @@ fn perform_assign( unreachable!() } Option::None - } else if ExecutionEnvironment::has_signal(&runtime.environment, symbol) { + }else { + // it is just a signal debug_assert!(accessing_information.signal_access.is_none()); debug_assert!(accessing_information.after_signal.is_empty()); @@ -1081,42 +1528,9 @@ fn perform_assign( &mut runtime.runtime_errors, &runtime.call_trace, )?; - let memory_response_for_signal_previous_value = SignalSlice::access_values( - reference_to_signal_content, - &accessing_information.before_signal, - ); - let signal_previous_value = treat_result_with_memory_error( - memory_response_for_signal_previous_value, - meta, - &mut runtime.runtime_errors, - &runtime.call_trace, - )?; - // Study the tags: add the new ones and copy their content. - /* - Cases: - - Inherance in arrays => We only have a tag in case it inherites the tag in all positions - - - Tag defined by user: - * Already with value defined by user => do not copy new values - * No value defined by user - - Already initialized: - * If same value as previous preserve - * If not set value to None - - No initialized: - * Set value to new one - - Tag not defined by user: - * Already initialized: - - If contains same tag with same value preserve - - No tag or different value => do not save tag or loose it - * No initialized: - - Save tag - - - */ - let previous_tags = mem::take(reference_to_tags); - + // Perform the tag assignment + let new_tags = if r_folded.tags.is_some() && op == AssignOp::AssignConstraintSignal{ r_folded.tags.clone().unwrap() } else{ @@ -1125,106 +1539,23 @@ fn perform_assign( let signal_is_init = SignalSlice::get_number_of_inserts(reference_to_signal_content) > 0; - for (tag, value) in previous_tags{ - let tag_state = reference_to_tags_defined.get(&tag).unwrap(); - if tag_state.defined{// is signal defined by user - if tag_state.value_defined{ - // already with value, store the same value - reference_to_tags.insert(tag, value); - } else{ - if signal_is_init { - // only keep value if same as previous - let to_store_value = if new_tags.contains_key(&tag){ - let value_new = new_tags.get(&tag).unwrap(); - if value != *value_new{ - None - } else{ - value - } - } else{ - None - }; - reference_to_tags.insert(tag, to_store_value); - } else{ - // always keep - if new_tags.contains_key(&tag){ - let value_new = new_tags.get(&tag).unwrap(); - reference_to_tags.insert(tag, value_new.clone()); - } else{ - reference_to_tags.insert(tag, None); - } - } - } - } else{ - // it is not defined by user - if new_tags.contains_key(&tag){ - let value_new = new_tags.get(&tag).unwrap(); - if value == *value_new{ - reference_to_tags.insert(tag, value); - } else{ - reference_to_tags_defined.remove(&tag); - } - } else{ - reference_to_tags_defined.remove(&tag); - } - } - } - - if !signal_is_init{ // first init, add new tags - for (tag, value) in new_tags{ - if !reference_to_tags.contains_key(&tag){ // in case it is a new tag (not defined by user) - reference_to_tags.insert(tag.clone(), value.clone()); - let state = TagState{defined: false, value_defined: false, complete: false}; - reference_to_tags_defined.insert(tag.clone(), state); - } - } - } - - + perform_tag_propagation(reference_to_tags, reference_to_tags_defined, &new_tags, signal_is_init); + + // Perform the signal assignment let r_slice = safe_unwrap_to_arithmetic_slice(r_folded, line!()); - let new_value_slice = &SignalSlice::new_with_route(r_slice.route(), &true); - - let correct_dims_result = SignalSlice::check_correct_dims( - &signal_previous_value, - &Vec::new(), - &new_value_slice, - true - ); + + let signal_assignment_response = perform_signal_assignment(reference_to_signal_content, &accessing_information.before_signal, &r_slice.route()); + treat_result_with_memory_error_void( - correct_dims_result, + signal_assignment_response, meta, &mut runtime.runtime_errors, &runtime.call_trace, )?; - for i in 0..SignalSlice::get_number_of_cells(&signal_previous_value){ - let signal_was_assigned = treat_result_with_memory_error( - SignalSlice::access_value_by_index(&signal_previous_value, i), - meta, - &mut runtime.runtime_errors, - &runtime.call_trace, - )?; - if signal_was_assigned { - let access_response = Result::Err(MemoryError::AssignmentError(TypeAssignmentError::MultipleAssignments)); - treat_result_with_memory_error( - access_response, - meta, - &mut runtime.runtime_errors, - &runtime.call_trace, - )?; - } - } - - - - let access_response = SignalSlice::insert_values( - reference_to_signal_content, - &accessing_information.before_signal, - &new_value_slice, - true - ); + // Update complete tags if completely init let signal_is_completely_initialized = SignalSlice::get_number_of_inserts(reference_to_signal_content) == SignalSlice::get_number_of_cells(reference_to_signal_content); @@ -1236,30 +1567,38 @@ fn perform_assign( for (tag, value) in reference_to_tags{ let tag_state = reference_to_tags_defined.get_mut(tag).unwrap(); tag_state.complete = true; - if let Option::Some(node) = actual_node{ - if !tag_state.value_defined{ - node.add_tag_signal(symbol, &tag, value.clone()); + match actual_node{ + ExecutedStructure::Template(node) =>{ + if !tag_state.value_defined{ + node.add_tag_signal(symbol, &tag, value.clone()); + } + }, + ExecutedStructure::Bus(_) =>{ + unreachable!(); + }, + ExecutedStructure::None => { + unreachable!(); } - } else{ - unreachable!(); } } } - - - treat_result_with_memory_error_void( - access_response, - meta, - &mut runtime.runtime_errors, - &runtime.call_trace, - )?; - - Option::Some(r_slice) - } else if ExecutionEnvironment::has_component(&runtime.environment, symbol) { - if accessing_information.tag_access.is_some() { - unreachable!() + // Get left arithmetic slice + let mut l_signal_names = Vec::new(); + unfold_signals(full_symbol, 0, r_slice.route(), &mut l_signal_names); + let mut l_expressions = Vec::new(); + for signal_name in l_signal_names{ + l_expressions.push(AExpr::Signal { symbol: signal_name }); } + let l_slice = AExpressionSlice::new_array(r_slice.route().to_vec(), l_expressions); + + // We return both the left and right slices + Option::Some((l_slice, r_slice)) + }} + else if ExecutionEnvironment::has_component(&runtime.environment, symbol) { + + let accessing_information = accessing_information.bus_access.as_ref().unwrap(); + let environment_response = ExecutionEnvironment::get_mut_component_res(&mut runtime.environment, symbol); let component_slice = treat_result_with_environment_error( environment_response, @@ -1275,18 +1614,29 @@ fn perform_assign( &Vec::new(), ) } else{ + // in case the component is undef then we do not perform any changes + // TODO: possible improvement? + if accessing_information.undefined{ + return Ok(None) + } + ComponentSlice::get_mut_reference_to_single_value( component_slice, - &accessing_information.before_signal, + &accessing_information.array_access, ) }; + let component = treat_result_with_memory_error( memory_response, meta, &mut runtime.runtime_errors, &runtime.call_trace, )?; - if accessing_information.signal_access.is_none() { + + + // We distinguish the different cases + if accessing_information.field_access.is_none() { + // case complete component assignment let (prenode_pointer, is_parallel) = safe_unwrap_to_valid_node_pointer(r_folded, line!()); let memory_result = ComponentRepresentation::preinitialize_component( component, @@ -1331,7 +1681,7 @@ fn perform_assign( } else{ ComponentSlice::get_mut_reference_to_single_value( component_slice, - &accessing_information.before_signal, + &accessing_information.array_access, ) }; let component = treat_result_with_memory_error( @@ -1352,116 +1702,718 @@ fn perform_assign( &mut runtime.runtime_errors, &runtime.call_trace, )?; - if let Option::Some(actual_node) = actual_node { - let data = SubComponentData { - name: symbol.to_string(), - is_parallel: component.is_parallel, - goes_to: node_pointer, - indexed_with: accessing_information.before_signal.clone(), - }; - actual_node.add_arrow(full_symbol.clone(), data); - } else { - unreachable!(); + + match actual_node{ + ExecutedStructure::Template(node) =>{ + let data = SubComponentData { + name: symbol.to_string(), + is_parallel: component.is_parallel, + goes_to: node_pointer, + indexed_with: accessing_information.array_access.clone(), + }; + node.add_arrow(full_symbol.clone(), data); + }, + ExecutedStructure::Bus(_) =>{ + unreachable!(); + }, + ExecutedStructure::None => { + unreachable!(); + } } } + Option::None } else { - let signal_accessed = accessing_information.signal_access.clone().unwrap(); - debug_assert!(FoldedValue::valid_arithmetic_slice(&r_folded)); - let arithmetic_slice = r_folded.arithmetic_slice.unwrap(); - let tags = if r_folded.tags.is_some() { - r_folded.tags.unwrap() - } else { - TagInfo::new() - }; - let memory_response = ComponentRepresentation::assign_value_to_signal( - component, - &signal_accessed, - &accessing_information.after_signal, - &arithmetic_slice.route(), - tags, - ); - treat_result_with_memory_error_void( - memory_response, - meta, - &mut runtime.runtime_errors, - &runtime.call_trace, - )?; - if !component.is_initialized && component.is_ready_initialize() { - // calls to execute and initialize the component - let pretemplate_info = runtime.exec_program.get_prenode_value( - component.node_pointer.unwrap() - ).unwrap(); - let inputs_tags = component.inputs_tags.clone(); + let remaining_access = accessing_information.remaining_access.as_ref().unwrap(); + let assigned_ae_slice = + if FoldedValue::valid_arithmetic_slice(&r_folded) + { + + // it is signal assignment of a input signal or a field of the bus + let signal_accessed = accessing_information.field_access.as_ref().unwrap(); + let arithmetic_slice = r_folded.arithmetic_slice.unwrap(); + let tags = if r_folded.tags.is_some() { + r_folded.tags.unwrap() + } else { + TagInfo::new() + }; + + let memory_response = if remaining_access.field_access.is_none(){ + ComponentRepresentation::assign_value_to_signal( + component, + &signal_accessed, + &remaining_access.array_access, + &arithmetic_slice.route(), + &tags, + ) + } else{ + let aux_slice = SignalSlice::new_with_route(arithmetic_slice.route(), &true); + ComponentRepresentation::assign_value_to_bus_field( + component, + &signal_accessed, + &remaining_access, + FoldedResult::Signal(aux_slice), + &tags, + ) + }; + treat_result_with_memory_error_void( + memory_response, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; - let folded_result = execute_template_call_complete( - pretemplate_info.template_name(), - pretemplate_info.parameter_instances().clone(), - inputs_tags, - program_archive, - runtime, - flags, - )?; - - let (node_pointer, _is_parallel) = safe_unwrap_to_valid_node_pointer(folded_result, line!()); + // Get left arithmetic slice + let mut l_signal_names = Vec::new(); + unfold_signals(full_symbol, 0, arithmetic_slice.route(), &mut l_signal_names); + let mut l_expressions = Vec::new(); + for signal_name in l_signal_names{ + l_expressions.push(AExpr::Signal { symbol: signal_name }); + } + let l_slice = AExpressionSlice::new_array(arithmetic_slice.route().to_vec(), l_expressions); + + (l_slice, arithmetic_slice) + } else if FoldedValue::valid_bus_slice(&r_folded){ + // it is a bus input + let bus_accessed = accessing_information.field_access.as_ref().unwrap(); + let (name_bus, assigned_bus_slice) = r_folded.bus_slice.unwrap(); + let tags = if r_folded.tags.is_some() { + r_folded.tags.unwrap() + } else { + TagInfo::new() + }; + + // Generate an arithmetic slice for the buses left and right + let mut signals_values_right: Vec = Vec::new(); + let mut signals_values_left: Vec = Vec::new(); + + + // Generate the arithmetic slices containing the signals + // We assume that the buses in the slice are all of the same type + // Use just the first to generate the bus accesses + + let mut inside_bus_signals = Vec::new(); - let environment_response = ExecutionEnvironment::get_mut_component_res(&mut runtime.environment, symbol); - let component_slice = treat_result_with_environment_error( - environment_response, - meta, - &mut runtime.runtime_errors, - &runtime.call_trace, - )?; - let memory_response = if is_anonymous_component { - ComponentSlice::get_mut_reference_to_single_value( - component_slice, - &Vec::new(), - ) + if BusSlice::get_number_of_cells(&assigned_bus_slice) > 0{ + let left_i = treat_result_with_memory_error( + BusSlice::get_reference_to_single_value_by_index(&assigned_bus_slice, 0), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + // generate the inside signals + inside_bus_signals = left_i.get_accesses_bus(""); + } + + for i in 0..BusSlice::get_number_of_cells(&assigned_bus_slice){ + + let access_index = treat_result_with_memory_error( + BusSlice::get_access_index(&assigned_bus_slice, i), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + let string_index = create_index_appendix(&access_index); + + for s in &inside_bus_signals{ + signals_values_right.push( + format!( + "{}{}{}", name_bus, string_index, s.clone() + )); + signals_values_left.push( + format!( + "{}{}{}", full_symbol, string_index, s.clone() + )); + } + } + + // Transform the signal names into AExpr + + let mut ae_signals_right = Vec::new(); + for signal_name in signals_values_right{ + ae_signals_right.push(AExpr::Signal { symbol: signal_name }); + } + let mut ae_signals_left = Vec::new(); + for signal_name in signals_values_left{ + ae_signals_left.push(AExpr::Signal { symbol: signal_name }); + } + + let memory_response = + if remaining_access.field_access.is_none() + { + + ComponentRepresentation::assign_value_to_bus( + component, + &bus_accessed, + &remaining_access.array_access, + assigned_bus_slice, + &tags, + ) + } else{ + ComponentRepresentation::assign_value_to_bus_field( + component, + &bus_accessed, + &remaining_access, + FoldedResult::Bus(assigned_bus_slice), + &tags, + ) + }; + treat_result_with_memory_error_void( + memory_response, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + + // Generate the ae expressions + + let ae_right = AExpressionSlice::new_array([ae_signals_right.len()].to_vec(), ae_signals_right); + let ae_left = AExpressionSlice::new_array([ae_signals_left.len()].to_vec(), ae_signals_left); + (ae_left, ae_right) + } else{ - ComponentSlice::get_mut_reference_to_single_value( - component_slice, - &accessing_information.before_signal, - ) + unreachable!(); }; - let component = treat_result_with_memory_error( - memory_response, + + if !component.is_initialized && component.is_ready_initialize() { + // calls to execute and initialize the component + let pretemplate_info = runtime.exec_program.get_prenode_value( + component.node_pointer.unwrap() + ).unwrap(); + let inputs_tags = component.inputs_tags.clone(); + + let folded_result = execute_template_call_complete( + pretemplate_info.template_name(), + pretemplate_info.parameter_instances().clone(), + inputs_tags, + program_archive, + runtime, + flags, + )?; + + let (node_pointer, _is_parallel) = safe_unwrap_to_valid_node_pointer(folded_result, line!()); + + let environment_response = ExecutionEnvironment::get_mut_component_res(&mut runtime.environment, symbol); + let component_slice = treat_result_with_environment_error( + environment_response, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + let memory_response = if is_anonymous_component { + ComponentSlice::get_mut_reference_to_single_value( + component_slice, + &Vec::new(), + ) + } else{ + ComponentSlice::get_mut_reference_to_single_value( + component_slice, + &accessing_information.array_access, + ) + }; + let component = treat_result_with_memory_error( + memory_response, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + + let init_result = ComponentRepresentation::initialize_component( + component, + node_pointer, + &mut runtime.exec_program, + ); + treat_result_with_memory_error_void( + init_result, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + match actual_node{ + ExecutedStructure::Template(node) =>{ + let data = SubComponentData { + name: symbol.to_string(), + goes_to: node_pointer, + is_parallel: component.is_parallel, + indexed_with: accessing_information.array_access.clone(), + }; + let component_symbol = create_component_symbol(symbol, &accessing_information.array_access); + node.add_arrow(component_symbol, data); + }, + ExecutedStructure::Bus(_) =>{ + unreachable!(); + }, + ExecutedStructure::None => { + unreachable!(); + } + } + } + Option::Some(assigned_ae_slice) + } + } else if ExecutionEnvironment::has_bus(&runtime.environment, symbol) + { + + // we check if it is an input bus, in that case all signals are initialized to true + let is_input_bus = ExecutionEnvironment::has_input_bus(&runtime.environment, symbol); + + let environment_response = ExecutionEnvironment::get_mut_bus_res(&mut runtime.environment, symbol); + + let (tags_info, tags_definition, bus_slice) = treat_result_with_environment_error( + environment_response, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + + let accessing_information = accessing_information.bus_access.as_ref().unwrap(); + + // in case the accessing information is undef do not perform changes + // TODO: possible improvement? + + if accessing_information.undefined{ + return Ok(None) + } + + if FoldedValue::valid_bus_node_pointer(&r_folded){ + // in this case we are performing an assigment of the type in the node_pointer + // to the bus in the left + + let bus_pointer = r_folded.bus_node_pointer.unwrap(); + // in this case we cannot assign to a single value of the array + debug_assert!(accessing_information.array_access.len() == 0); + debug_assert!(accessing_information.field_access.is_none()); + + + for i in 0..BusSlice::get_number_of_cells(&bus_slice){ + let mut value_left = treat_result_with_memory_error( + BusSlice::get_mut_reference_to_single_value_by_index(bus_slice, i), meta, &mut runtime.runtime_errors, &runtime.call_trace, )?; - let init_result = ComponentRepresentation::initialize_component( - component, - node_pointer, - &mut runtime.exec_program, + let memory_result = BusRepresentation::initialize_bus( + &mut value_left, + bus_pointer, + &runtime.exec_program, + is_input_bus ); treat_result_with_memory_error_void( - init_result, + memory_result, meta, &mut runtime.runtime_errors, &runtime.call_trace, )?; - if let Option::Some(actual_node) = actual_node { - let data = SubComponentData { + + } + let bus_info = runtime.exec_program.get_bus_node(bus_pointer).unwrap(); + let size = bus_info.size; + match actual_node{ + ExecutedStructure::Template(node) =>{ + let data = BusData { name: symbol.to_string(), - goes_to: node_pointer, - is_parallel: component.is_parallel, - indexed_with: accessing_information.before_signal.clone(), + goes_to: bus_pointer, + size, }; - let component_symbol = create_component_symbol(symbol, &accessing_information); - actual_node.add_arrow(component_symbol, data); - } else { + let component_symbol = create_array_accessed_symbol(symbol, &accessing_information.array_access); + node.add_bus_arrow(component_symbol, data); + }, + ExecutedStructure::Bus(node) =>{ + let data = BusData { + name: symbol.to_string(), + goes_to: bus_pointer, + size + }; + let component_symbol = create_array_accessed_symbol(symbol, &accessing_information.array_access); + node.add_bus_arrow(component_symbol, data); + }, + ExecutedStructure::None => { unreachable!(); } } - Option::Some(arithmetic_slice) + + None + } else if FoldedValue::valid_arithmetic_slice(&r_folded){ + // case assigning a field of the bus + if meta.get_type_knowledge().is_signal(){ + + let mut value_left = treat_result_with_memory_error( + BusSlice::access_values_by_mut_reference(bus_slice, &accessing_information.array_access), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + + assert!(value_left.len() == 1); + let single_bus = value_left.get_mut(0).unwrap(); + assert!(accessing_information.field_access.is_some()); + + let arithmetic_slice = r_folded.arithmetic_slice.unwrap(); + let tags = if r_folded.tags.is_some() { + r_folded.tags.unwrap() + } else { + TagInfo::new() + }; + + let memory_response = single_bus.assign_value_to_field( + accessing_information.field_access.as_ref().unwrap(), + accessing_information.remaining_access.as_ref().unwrap(), + FoldedArgument::Signal(&arithmetic_slice.route().to_vec()), + Some(tags), + false + ); + treat_result_with_memory_error_void( + memory_response, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + + // Get left arithmetic slice + let mut l_signal_names = Vec::new(); + unfold_signals(full_symbol, 0, arithmetic_slice.route(), &mut l_signal_names); + let mut l_expressions = Vec::new(); + for signal_name in l_signal_names{ + l_expressions.push(AExpr::Signal { symbol: signal_name }); + } + let l_slice = AExpressionSlice::new_array(arithmetic_slice.route().to_vec(), l_expressions); + Some((l_slice, arithmetic_slice)) + + } else if meta.get_type_knowledge().is_tag(){ + // in case we are assigning a tag of the complete bus + // or one of its fields + assert!(accessing_information.array_access.len() == 0); + assert!(accessing_information.field_access.is_some()); + if accessing_information.remaining_access.as_ref().unwrap().field_access.is_none(){ + // case tag of the complete bus + let tag = accessing_information.field_access.as_ref().unwrap(); + let environment_response = ExecutionEnvironment::get_mut_bus_res(&mut runtime.environment, symbol); + let (reference_to_tags, reference_to_tags_defined, bus_content) = treat_result_with_environment_error( + environment_response, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + + for i in 0..BusSlice::get_number_of_cells(bus_content){ + let accessed_bus = treat_result_with_memory_error( + BusSlice::get_reference_to_single_value_by_index(bus_content, i), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + if accessed_bus.has_assignment(){ + treat_result_with_memory_error( + Result::Err(MemoryError::AssignmentTagAfterInit), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )? + } + } + + if let Some(a_slice) = r_folded.arithmetic_slice { + let value = AExpressionSlice::unwrap_to_single(a_slice); + match value { + ArithmeticExpressionGen::Number { value } => { + let possible_tag = reference_to_tags.get(&tag.clone()); + if let Some(val) = possible_tag { + if let Some(_) = val { + treat_result_with_memory_error( + Result::Err(MemoryError::AssignmentTagTwice), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )? + } else { // we add the info saying that the tag is defined + reference_to_tags.insert(tag.clone(), Option::Some(value.clone())); + let tag_state = reference_to_tags_defined.get_mut(tag).unwrap(); + tag_state.value_defined = true; + match actual_node{ + ExecutedStructure::Template(node) =>{ + node.add_tag_signal(symbol, &tag, Some(value)); + }, + ExecutedStructure::Bus(node) =>{ + node.add_tag_signal(symbol, &tag, Some(value)); + }, + ExecutedStructure::None => { + unreachable!(); + } + } + } + } else {unreachable!()} + }, + _ => unreachable!(), + } + } + else { + unreachable!() + } + } else{ + // in case it is a tag of one its fields + let mut value_left = treat_result_with_memory_error( + BusSlice::access_values_by_mut_reference(bus_slice, &accessing_information.array_access), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + + assert!(value_left.len() == 1); + let single_bus = value_left.get_mut(0).unwrap(); + + + assert!(accessing_information.field_access.is_some()); + let arithmetic_slice = r_folded.arithmetic_slice.unwrap(); + let value_aux = AExpressionSlice::unwrap_to_single(arithmetic_slice); + let value = if let ArithmeticExpressionGen::Number { value } = value_aux { + value + } else { + unreachable!(); + }; + let memory_response = single_bus.assign_value_to_field( + accessing_information.field_access.as_ref().unwrap(), + accessing_information.remaining_access.as_ref().unwrap(), + FoldedArgument::Tag(&value), + None, + false + ); + treat_result_with_memory_error_void( + memory_response, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + } + None + } else{ + unreachable!(); + } + } else if FoldedValue::valid_bus_slice(&r_folded){ + let (name_bus, assigned_bus_slice) = r_folded.bus_slice.as_ref().unwrap(); + // case assigning a bus (complete or field) + if accessing_information.field_access.is_none(){ + + // We assign the tags + if r_folded.tags.is_some(){ + + let mut bus_is_init = false; + for i in 0..BusSlice::get_number_of_cells(bus_slice){ + let accessed_bus = treat_result_with_memory_error( + BusSlice::get_reference_to_single_value_by_index(bus_slice, i), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + bus_is_init |= accessed_bus.has_assignment(); + } + + // Perform the tag assignment + let new_tags = r_folded.tags.unwrap(); + perform_tag_propagation(tags_info, tags_definition, &new_tags, bus_is_init); + + } + + // We assign the original buses + let bus_assignment_response = perform_bus_assignment(bus_slice, &accessing_information.array_access, assigned_bus_slice, false); + treat_result_with_memory_error_void( + bus_assignment_response, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + + // Generate an arithmetic slice for the accessed buses + let mut signals_values_left: Vec = Vec::new(); + let mut signals_values_right = Vec::new(); + + // We assume that the buses in the slice are all of the same type + // Use just the first to generate the bus accesses + + let mut inside_bus_signals = Vec::new(); + + if BusSlice::get_number_of_cells(&assigned_bus_slice) > 0{ + let left_i = treat_result_with_memory_error( + BusSlice::get_reference_to_single_value_by_index(&assigned_bus_slice, 0), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + // generate the inside signals + inside_bus_signals = left_i.get_accesses_bus(""); + } + + for i in 0..BusSlice::get_number_of_cells(&assigned_bus_slice){ + + let access_index = treat_result_with_memory_error( + BusSlice::get_access_index(&assigned_bus_slice, i), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + let string_index = create_index_appendix(&access_index); + + for s in &inside_bus_signals{ + signals_values_right.push( + format!( + "{}{}{}", name_bus, string_index, s.clone() + )); + signals_values_left.push( + format!( + "{}{}{}", full_symbol, string_index, s.clone() + )); + } + } + + // Transform the signal names into AExpr + let mut ae_signals_left = Vec::new(); + for signal_name in signals_values_left{ + ae_signals_left.push(AExpr::Signal { symbol: signal_name }); + } + let mut ae_signals_right = Vec::new(); + for signal_name in signals_values_right{ + ae_signals_right.push(AExpr::Signal { symbol: signal_name }); + } + + // Update the final tags + let mut bus_is_completely_init = true; + for i in 0..BusSlice::get_number_of_cells(bus_slice){ + let accessed_bus = treat_result_with_memory_error( + BusSlice::get_reference_to_single_value_by_index(bus_slice, i), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + bus_is_completely_init &= accessed_bus.has_assignment(); + } + if bus_is_completely_init{ + for (tag, value) in tags_info{ + let tag_state = tags_definition.get_mut(tag).unwrap(); + tag_state.complete = true; + match actual_node{ + ExecutedStructure::Template(node) =>{ + if !tag_state.value_defined{ + node.add_tag_signal(symbol, &tag, value.clone()); + } + }, + ExecutedStructure::Bus(_) =>{ + unreachable!(); + }, + ExecutedStructure::None => { + unreachable!(); + } + } + } + } + + // Update the left slice + let l_slice = AExpressionSlice::new_array([ae_signals_left.len()].to_vec(), ae_signals_left); + let r_slice = AExpressionSlice::new_array([ae_signals_right.len()].to_vec(), ae_signals_right); + + + Some((l_slice, r_slice)) + } else{ + + let mut value_left = treat_result_with_memory_error( + BusSlice::access_values_by_mut_reference(bus_slice, &accessing_information.array_access), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + + assert!(value_left.len() == 1); + let single_bus = value_left.get_mut(0).unwrap(); + + assert!(accessing_information.field_access.is_some()); + let (name_bus, bus_slice) = r_folded.bus_slice.as_ref().unwrap(); + let tags = if r_folded.tags.is_some() { + r_folded.tags.unwrap() + } else { + TagInfo::new() + }; + + let memory_response = single_bus.assign_value_to_field( + accessing_information.field_access.as_ref().unwrap(), + accessing_information.remaining_access.as_ref().unwrap(), + FoldedArgument::Bus(bus_slice), + Some(tags), + false + ); + treat_result_with_memory_error_void( + memory_response, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + + // Update the left and right slices + let mut signals_values_left: Vec = Vec::new(); + let mut signals_values_right: Vec = Vec::new(); + + // Generate the arithmetic slices containing the signals + // We assume that the buses in the slice are all of the same type + // Use just the first to generate the bus accesses + + let mut inside_bus_signals = Vec::new(); + + if BusSlice::get_number_of_cells(&assigned_bus_slice) > 0{ + let left_i = treat_result_with_memory_error( + BusSlice::get_reference_to_single_value_by_index(&assigned_bus_slice, 0), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + // generate the inside signals + inside_bus_signals = left_i.get_accesses_bus(""); + } + + for i in 0..BusSlice::get_number_of_cells(&assigned_bus_slice){ + + let access_index = treat_result_with_memory_error( + BusSlice::get_access_index(&assigned_bus_slice, i), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + let string_index = create_index_appendix(&access_index); + + for s in &inside_bus_signals{ + signals_values_right.push( + format!( + "{}{}{}", name_bus, string_index, s.clone() + )); + signals_values_left.push( + format!( + "{}{}{}", full_symbol, string_index, s.clone() + )); + } + } + + // Transform the signal names into AExpr + + let mut ae_signals_left = Vec::new(); + for signal_name in signals_values_left{ + ae_signals_left.push(AExpr::Signal { symbol: signal_name }); + } + let mut ae_signals_right = Vec::new(); + for signal_name in signals_values_right{ + ae_signals_right.push(AExpr::Signal { symbol: signal_name }); + } + let l_slice = AExpressionSlice::new_array([ae_signals_left.len()].to_vec(), ae_signals_left); + let r_slice = AExpressionSlice::new_array([ae_signals_right.len()].to_vec(), ae_signals_right); + Some((l_slice, r_slice)) + } + } else{ + + unreachable!() } + + } else { unreachable!(); }; - if let Option::Some(arithmetic_slice) = possible_arithmetic_slice { - let ret = Constrained { left: full_symbol, right: arithmetic_slice }; + if let Option::Some((arithmetic_slice_left, arithmetic_slice_right)) = possible_arithmetic_slices { + let ret = Constrained { left: arithmetic_slice_left, right: arithmetic_slice_right }; Result::Ok(Some(ret)) } else { Result::Ok(None) @@ -1485,16 +2437,20 @@ fn execute_conditional_statement( AExpr::get_boolean_equivalence(&ae_cond, runtime.constants.get_p()); if let Some(cond_bool_value) = possible_cond_bool_value { let (ret_value, can_simplify) = match false_case { - Some(else_stmt) if !cond_bool_value => { + Option::Some(else_stmt) if !cond_bool_value => { execute_statement(else_stmt, program_archive, runtime, actual_node, flags)? } - None if !cond_bool_value => (None, true), + Option::None if !cond_bool_value => (None, true), _ => execute_statement(true_case, program_archive, runtime, actual_node, flags)?, }; Result::Ok((ret_value, can_simplify, Option::Some(cond_bool_value))) } else { let previous_block_type = runtime.block_type; runtime.block_type = BlockType::Unknown; + // TODO: here instead of executing both branches what we do is to store the values + // that we assign in each one of the branches and assign later: if we assign in both + // of them a signal we return an error. If we assign in just one then we dont return error + // (maybe a warning indicating that the variable may not get assigned in the if) let (mut ret_value, mut can_simplify) = execute_statement(true_case, program_archive, runtime, actual_node, flags)?; if let Option::Some(else_stmt) = false_case { let (else_ret, can_simplify_else) = execute_statement(else_stmt, program_archive, runtime, actual_node, flags)?; @@ -1530,6 +2486,19 @@ fn execute_sequence_of_statements( Result::Ok((Option::None, can_be_simplified)) } +fn execute_sequence_of_bus_statements( + stmts: &[Statement], + program_archive: &ProgramArchive, + runtime: &mut RuntimeInformation, + actual_node: &mut ExecutedBus, + flags: FlagsExecution, +) -> Result<(), ()> { + for stmt in stmts.iter() { + execute_bus_statement(stmt, program_archive, runtime, actual_node, flags)?; + } + Result::Ok(()) +} + fn execute_delayed_declarations( program_archive: &ProgramArchive, runtime: &mut RuntimeInformation, @@ -1570,12 +2539,15 @@ fn execute_delayed_declarations( //************************************************* Expression execution support ************************************************* -fn create_component_symbol(symbol: &str, access_information: &AccessingInformation) -> String { +fn create_array_accessed_symbol(symbol: &str, array_access: &Vec) -> String { let mut appendix = "".to_string(); - let bf_signal = create_index_appendix(&access_information.before_signal); - appendix.push_str(&bf_signal); + let access = create_index_appendix(array_access); + appendix.push_str(&access); format!("{}{}", symbol, appendix) } +fn create_component_symbol(symbol: &str, access_information: &Vec) -> String { + create_array_accessed_symbol(symbol, &access_information) +} fn create_symbol(symbol: &str, access_information: &AccessingInformation) -> String { let mut appendix = "".to_string(); @@ -1590,6 +2562,22 @@ fn create_symbol(symbol: &str, access_information: &AccessingInformation) -> Str format!("{}{}", symbol, appendix) } +fn create_symbol_bus(symbol: &str, access_information: &AccessingInformationBus) -> String { + let mut appendix = symbol.to_string(); + let bf_field = create_index_appendix(&access_information.array_access); + appendix.push_str(&bf_field); + if let Option::Some(field) = &access_information.field_access { + let field = format!(".{}", field); + appendix.push_str(&field); + } + if let Option::Some(after_field) = &access_information.remaining_access { + create_symbol_bus(&appendix, after_field) + } else{ + appendix + } +} + + fn create_index_appendix(indexing: &[usize]) -> String { let mut appendix = "".to_string(); for index in indexing { @@ -1599,6 +2587,14 @@ fn create_index_appendix(indexing: &[usize]) -> String { appendix } + +// fn create_symbols_form_access_bus(symbol: &str, access_information: &AccessingInformationBus,runtime: &RuntimeInformation)-> Vec{ +// let prefix = create_symbol_bus(symbol, access_information); +// if ExecutionEnvironment::has_bus(&runtime.environment, symbol) { +// execute_signal(meta, name, access, program_archive, runtime, flags)? +// } +// } + fn execute_variable( meta: &Meta, symbol: &str, @@ -1713,49 +2709,208 @@ fn execute_signal( &runtime.call_trace, )?; - let mut tags_propagated = TagInfo::new(); - for (tag, value) in tags{ - let state = tags_definitions.get(tag).unwrap(); - if state.value_defined || state.complete{ - tags_propagated.insert(tag.clone(), value.clone()); - } else if state.defined{ - tags_propagated.insert(tag.clone(), None); - } - } + // check which tags are propagated + let tags_propagated = compute_propagated_tags(tags, tags_definitions); + + Result::Ok(FoldedValue { + arithmetic_slice: Option::Some(arith_slice), + tags: Option::Some(tags_propagated), + ..FoldedValue::default() + }) + } +} + +fn signal_to_arith(symbol: String, slice: SignalSlice) -> Result { + let mut expressions = vec![]; + let (route, values) = slice.destruct(); + let mut symbols = vec![]; + unfold_signals(symbol, 0, &route, &mut symbols); + let mut index = 0; + while index < symbols.len() && values[index] { + expressions.push(AExpr::Signal { symbol: symbols[index].clone() }); + index += 1; + } + if index == symbols.len() { + Result::Ok(AExpressionSlice::new_array(route, expressions)) + } else { + Result::Err(MemoryError::InvalidAccess(TypeInvalidAccess::NoInitializedSignal)) + } +} + +fn unfold_signals(current: String, dim: usize, lengths: &[usize], result: &mut Vec) { + if dim == lengths.len() { + result.push(current); + } else { + for i in 0..lengths[dim] { + unfold_signals(format!("{}[{}]", current, i), dim + 1, lengths, result) + } + } +} + +fn execute_bus( + meta: &Meta, + symbol: &str, + access: &[Access], + program_archive: &ProgramArchive, + runtime: &mut RuntimeInformation, + flags: FlagsExecution +) -> Result { + let access_information = treat_accessing_bus(meta, access, program_archive, runtime, flags)?; + if access_information.undefined { + let arithmetic_slice = Option::Some(AExpressionSlice::new(&AExpr::NonQuadratic)); + return Result::Ok(FoldedValue { arithmetic_slice, ..FoldedValue::default() }); + } + let environment_response = + ExecutionEnvironment::get_bus_res(&runtime.environment, symbol); + let (tags, tags_definitions, bus_slice) = treat_result_with_environment_error( + environment_response, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + + let memory_response = BusSlice::access_values(&bus_slice, &access_information.array_access); + let bus_slice = treat_result_with_memory_error( + memory_response, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + + if access_information.field_access.is_none() { + + // Case we are accessing the complete bus or array of buses + let symbol = create_symbol_bus(symbol, &access_information); + + // Compute which tags are propagated + let tags_propagated = compute_propagated_tags(tags, tags_definitions); + + // Check that all the buses are completely assigned + + for i in 0..BusSlice::get_number_of_cells(&bus_slice){ + let value_left = treat_result_with_memory_error( + BusSlice::get_reference_to_single_value_by_index(&bus_slice, i), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + + if value_left.has_unassigned_fields(){ + treat_result_with_memory_error( + Result::Err(MemoryError::InvalidAccess(TypeInvalidAccess::NoInitializedBus)), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + } + } + + Result::Ok(FoldedValue{bus_slice: Some((symbol.to_string(), bus_slice)), tags: Some(tags_propagated), ..FoldedValue::default()}) + } else if tags.contains_key(access_information.field_access.as_ref().unwrap()){ + // case tags + let acc = access_information.field_access.unwrap(); + let value_tag = tags.get(&acc).unwrap(); + let state = tags_definitions.get(&acc).unwrap(); + if let Some(value_tag) = value_tag { // tag has value + // access only allowed when (1) it is value defined by user or (2) it is completely assigned + if state.value_defined || state.complete{ + let a_value = AExpr::Number { value: value_tag.clone() }; + let ae_slice = AExpressionSlice::new(&a_value); + Result::Ok(FoldedValue { arithmetic_slice: Option::Some(ae_slice), ..FoldedValue::default() }) + } else{ + let error = MemoryError::TagValueNotInitializedAccess; + treat_result_with_memory_error( + Result::Err(error), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )? + } + + } + else { + let error = MemoryError::TagValueNotInitializedAccess; + treat_result_with_memory_error( + Result::Err(error), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )? + } + + } else{ + // access to the field or tag of a field + + let resulting_bus = safe_unwrap_to_single(bus_slice, line!()); + let symbol = create_symbol_bus(symbol, &access_information); + + // access to the field + let field_name = access_information.field_access.as_ref().unwrap(); + let remaining_access = access_information.remaining_access.as_ref().unwrap(); + + + let (tags, result) = treat_result_with_memory_error( + resulting_bus.get_field(field_name, remaining_access), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + + // match the result and generate the output + match result{ + FoldedResult::Signal(signals) =>{ + // Generate signal slice and check that all assigned + let result = signal_to_arith(symbol, signals) + .map(|s| FoldedValue { + arithmetic_slice: Option::Some(s), + tags: Option::Some(tags.unwrap()), + ..FoldedValue::default() + }); + treat_result_with_memory_error( + result, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + ) + }, + FoldedResult::Bus(buses) =>{ + // Check that all the buses are completely assigned - Result::Ok(FoldedValue { - arithmetic_slice: Option::Some(arith_slice), - tags: Option::Some(tags_propagated), - ..FoldedValue::default() - }) - } -} + for i in 0..BusSlice::get_number_of_cells(&buses){ + let value_left = treat_result_with_memory_error( + BusSlice::get_reference_to_single_value_by_index(&buses, i), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + + if value_left.has_unassigned_fields(){ + treat_result_with_memory_error( + Result::Err(MemoryError::InvalidAccess(TypeInvalidAccess::NoInitializedBus)), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + } + } + Ok(FoldedValue { + bus_slice: Option::Some((symbol, buses)), + tags: Option::Some(tags.unwrap()), + ..FoldedValue::default() + }) -fn signal_to_arith(symbol: String, slice: SignalSlice) -> Result { - let mut expressions = vec![]; - let (route, values) = slice.destruct(); - let mut symbols = vec![]; - unfold_signals(symbol, 0, &route, &mut symbols); - let mut index = 0; - while index < symbols.len() && values[index] { - expressions.push(AExpr::Signal { symbol: symbols[index].clone() }); - index += 1; - } - if index == symbols.len() { - Result::Ok(AExpressionSlice::new_array(route, expressions)) - } else { - Result::Err(MemoryError::InvalidAccess(TypeInvalidAccess::NoInitializedSignal)) - } -} + }, + FoldedResult::Tag(value) =>{ + // return the tag value + let a_value = AExpr::Number { value }; + let ae_slice = AExpressionSlice::new(&a_value); + Result::Ok(FoldedValue { arithmetic_slice: Option::Some(ae_slice), ..FoldedValue::default() }) -fn unfold_signals(current: String, dim: usize, lengths: &[usize], result: &mut Vec) { - if dim == lengths.len() { - result.push(current); - } else { - for i in 0..lengths[dim] { - unfold_signals(format!("{}[{}]", current, i), dim + 1, lengths, result) + } } + } + } fn execute_component( @@ -1766,11 +2921,13 @@ fn execute_component( runtime: &mut RuntimeInformation, flags: FlagsExecution ) -> Result { - let access_information = treat_accessing(meta, access, program_archive, runtime, flags)?; + + let access_information = treat_accessing_bus(meta, access, program_archive, runtime, flags)?; if access_information.undefined { let arithmetic_slice = Option::Some(AExpressionSlice::new(&AExpr::NonQuadratic)); return Result::Ok(FoldedValue { arithmetic_slice, ..FoldedValue::default() }); } + let environment_response = ExecutionEnvironment::get_component_res(&runtime.environment, symbol); let component_slice = treat_result_with_environment_error( @@ -1782,7 +2939,7 @@ fn execute_component( let memory_response = if runtime.anonymous_components.contains_key(symbol) { ComponentSlice::access_values(component_slice, &Vec::new()) } else{ - ComponentSlice::access_values(component_slice, &access_information.before_signal) + ComponentSlice::access_values(component_slice, &access_information.array_access) }; let slice_result = treat_result_with_memory_error( memory_response, @@ -1792,83 +2949,88 @@ fn execute_component( )?; let resulting_component = safe_unwrap_to_single(slice_result, line!()); - if let Some(acc) = access_information.tag_access { - let (tags_signal, _) = treat_result_with_memory_error( - resulting_component.get_signal(&access_information.signal_access.unwrap()), + if let Option::Some(signal_name) = &access_information.field_access { + let remaining_access = access_information.remaining_access.as_ref().unwrap(); + let symbol = create_symbol_bus(symbol, &access_information); + + let (tags, result) = treat_result_with_memory_error( + resulting_component.get_io_value(signal_name, remaining_access), meta, &mut runtime.runtime_errors, &runtime.call_trace, )?; - if tags_signal.contains_key(&acc) { - let value_tag = tags_signal.get(&acc).unwrap(); - if let Some(value_tag) = value_tag { - let a_value = AExpr::Number { value: value_tag.clone() }; - let ae_slice = AExpressionSlice::new(&a_value); - Result::Ok(FoldedValue { arithmetic_slice: Option::Some(ae_slice), ..FoldedValue::default() }) - } - else { - let error = MemoryError::TagValueNotInitializedAccess; + match result{ + FoldedResult::Signal(signals) =>{ + let result = signal_to_arith(symbol, signals) + .map(|s| FoldedValue { + arithmetic_slice: Option::Some(s), + tags: Option::Some(tags.unwrap()), + ..FoldedValue::default() + }); treat_result_with_memory_error( - Result::Err(error), + result, meta, &mut runtime.runtime_errors, &runtime.call_trace, - )? + ) + }, + FoldedResult::Bus(buses) =>{ + // Check that all the buses are completely assigned + + for i in 0..BusSlice::get_number_of_cells(&buses){ + let value_left = treat_result_with_memory_error( + BusSlice::get_reference_to_single_value_by_index(&buses, i), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + + if value_left.has_unassigned_fields(){ + treat_result_with_memory_error( + Result::Err(MemoryError::InvalidAccess(TypeInvalidAccess::NoInitializedBus)), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + } + } + Ok(FoldedValue { + bus_slice: Option::Some((symbol, buses)), + tags: Option::Some(tags.unwrap()), + ..FoldedValue::default() + }) + + }, + FoldedResult::Tag(value) =>{ + let a_value = AExpr::Number { value }; + let ae_slice = AExpressionSlice::new(&a_value); + Result::Ok(FoldedValue { arithmetic_slice: Option::Some(ae_slice), ..FoldedValue::default() }) + } - } else { - unreachable!() } - } - else if let Option::Some(signal_name) = &access_information.signal_access { - let access_after_signal = &access_information.after_signal; - let (tags_signal, signal) = treat_result_with_memory_error( - resulting_component.get_signal(signal_name), - meta, - &mut runtime.runtime_errors, - &runtime.call_trace, - )?; - let slice = SignalSlice::access_values(signal, &access_after_signal); - let slice = treat_result_with_memory_error( - slice, - meta, - &mut runtime.runtime_errors, - &runtime.call_trace, - )?; - let symbol = create_symbol(symbol, &access_information); - let result = signal_to_arith(symbol, slice) - .map(|s| FoldedValue { - arithmetic_slice: Option::Some(s), - tags: Option::Some(tags_signal.clone()), - ..FoldedValue::default() - }); - treat_result_with_memory_error( - result, - meta, - &mut runtime.runtime_errors, - &runtime.call_trace, - ) } else { - let read_result = if resulting_component.is_ready_initialize() { - Result::Ok(resulting_component) - } else { - Result::Err(MemoryError::InvalidAccess(TypeInvalidAccess::NoInitializedComponent)) - }; - - let checked_component = treat_result_with_memory_error( - read_result, - meta, - &mut runtime.runtime_errors, - &runtime.call_trace, - )?; - - Result::Ok(FoldedValue { - node_pointer: checked_component.node_pointer, - is_parallel: Some(false), - ..FoldedValue::default() - }) - } + let read_result = if resulting_component.is_ready_initialize() { + Result::Ok(resulting_component) + } else { + Result::Err(MemoryError::InvalidAccess(TypeInvalidAccess::NoInitializedComponent)) + }; + + let checked_component = treat_result_with_memory_error( + read_result, + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + + Result::Ok(FoldedValue { + node_pointer: checked_component.node_pointer, + is_parallel: Some(false), + ..FoldedValue::default() + }) + } + } fn prepare_environment_for_call( @@ -1877,10 +3039,15 @@ fn prepare_environment_for_call( program_archive: &ProgramArchive, ) -> ExecutionEnvironment { let functions = program_archive.get_function_names(); + let templates = program_archive.get_template_names(); + let arg_names = if functions.contains(id) { program_archive.get_function_data(id).get_name_of_params() - } else { + } else if templates.contains(id){ program_archive.get_template_data(id).get_name_of_params() + } else { + // case bus + program_archive.get_bus_data(id).get_name_of_params() }; let mut environment = ExecutionEnvironment::new(); @@ -1908,6 +3075,8 @@ fn execute_function_call( Result::Ok((return_value, can_be_simplified)) } + + fn execute_template_call( id: &str, parameter_values: Vec, @@ -2011,11 +3180,11 @@ fn preexecute_template_call( for (name, info_input) in inputs { - inputs_to_tags.insert(name.clone(), info_input.1.clone()); + inputs_to_tags.insert(name.clone(), info_input.get_tags().clone()); } for (name, info_output) in outputs { - outputs_to_tags.insert(name.clone(), info_output.1.clone()); + outputs_to_tags.insert(name.clone(), info_output.get_tags().clone()); } let node_wrap = Option::Some(PreExecutedTemplate::new( @@ -2030,6 +3199,60 @@ fn preexecute_template_call( Result::Ok(FoldedValue { node_pointer: Option::Some(node_pointer), is_parallel: Option::Some(false), ..FoldedValue::default() }) } +fn execute_bus_call( + id: &str, + parameter_values: Vec, + program_archive: &ProgramArchive, + runtime: &mut RuntimeInformation, + flags: FlagsExecution, +) -> Result { + debug_assert!(runtime.block_type == BlockType::Known); + + let args_names = program_archive.get_bus_data(id).get_name_of_params(); + let bus_body = program_archive.get_bus_data(id).get_body_as_vec(); + let mut args_to_values = BTreeMap::new(); + debug_assert_eq!(args_names.len(), parameter_values.len()); + let mut instantiation_name = format!("{}(", id); + let mut not_empty_name = false; + + for (name, value) in args_names.iter().zip(parameter_values) { + instantiation_name.push_str(&format!("{},", value.to_string())); + not_empty_name = true; + args_to_values.insert(name.clone(), value.clone()); + } + + if not_empty_name { + instantiation_name.pop(); + } + instantiation_name.push(')'); + + let existent_node = runtime.exec_program.identify_bus_node(id, &args_to_values); + let node_pointer = if let Option::Some(pointer) = existent_node { + pointer + } else { + let analysis = + std::mem::replace(&mut runtime.analysis, Analysis::new(program_archive.id_max)); + let mut node = ExecutedBus::new( + id.to_string(), + instantiation_name, + args_to_values, + ); + execute_sequence_of_bus_statements( + bus_body, + program_archive, + runtime, + &mut node, + flags, + )?; + + + let analysis = std::mem::replace(&mut runtime.analysis, analysis); + let node_pointer = runtime.exec_program.add_bus_node_to_scheme(node, analysis); + node_pointer + }; + Result::Ok(FoldedValue { bus_node_pointer: Option::Some(node_pointer), ..FoldedValue::default() }) +} + fn execute_infix_op( meta: &Meta, infix: ExpressionInfixOpcode, @@ -2079,7 +3302,7 @@ fn execute_prefix_op( let result = match prefix_op { BoolNot => AExpr::not(value, field), Sub => AExpr::prefix_sub(value, field), - Complement => AExpr::complement_254(value, field), + Complement => AExpr::complement(value, field), }; Result::Ok(result) } @@ -2175,23 +3398,6 @@ fn cast_index(ae_index: &AExpr) -> Option { } } -/* - Usable representation of a series of accesses performed over a symbol. - AccessingInformation { - pub undefined: bool ===> true if one of the index values could not be transformed into a SliceCapacity during the process, - pub before_signal: Vec, - pub signal_access: Option ==> may not appear, - pub after_signal: Vec - pub tag_access: Option ==> may not appear, - } -*/ -struct AccessingInformation { - pub undefined: bool, - pub before_signal: Vec, - pub signal_access: Option, - pub after_signal: Vec, - pub tag_access: Option -} fn treat_accessing( meta: &Meta, access: &[Access], @@ -2230,6 +3436,74 @@ fn treat_accessing( Result::Ok(AccessingInformation { undefined, before_signal, after_signal, signal_access, tag_access}) } + +fn treat_accessing_bus( + meta: &Meta, + access: &[Access], + program_archive: &ProgramArchive, + runtime: &mut RuntimeInformation, + flags: FlagsExecution +) -> Result { + + fn treat_accessing_bus_index( + index: usize, + meta: &Meta, + access: &[Access], + program_archive: &ProgramArchive, + runtime: &mut RuntimeInformation, + flags: FlagsExecution + ) -> Result{ + + let (ae_before_signal, field_access, signal_index) = + treat_indexing(index, access, program_archive, runtime, flags)?; + + treat_result_with_memory_error( + valid_indexing(&ae_before_signal), + meta, + &mut runtime.runtime_errors, + &runtime.call_trace, + )?; + + let mut remaining_access = if signal_index < access.len(){ + Some(Box::new( + treat_accessing_bus_index(signal_index + 1, meta, access, program_archive, runtime, flags)?) + ) + } else{ + None + }; + + let possible_before_indexing = cast_indexing(&ae_before_signal); + + let remaining_access_undefined = remaining_access.is_some() && remaining_access.as_ref().unwrap().undefined; + + let undefined = possible_before_indexing.is_none() || remaining_access_undefined; + + let array_access = if undefined { + Vec::new() + } else { + possible_before_indexing.unwrap() + }; + if undefined{ + remaining_access = None + }; + + Result::Ok(AccessingInformationBus { undefined, array_access, remaining_access, field_access}) + + } + + treat_accessing_bus_index( + 0, + meta, + access, + program_archive, + runtime, + flags + ) + +} + + + //************************************************* Safe transformations ************************************************* fn safe_unwrap_to_single_arithmetic_expression(folded_value: FoldedValue, line: u32) -> AExpr { @@ -2244,6 +3518,14 @@ fn safe_unwrap_to_valid_node_pointer(folded_value: FoldedValue, line: u32) -> (N debug_assert!(FoldedValue::valid_node_pointer(&folded_value), "Caused by call at {}", line); (folded_value.node_pointer.unwrap(), folded_value.is_parallel.unwrap()) } +fn safe_unwrap_to_valid_bus_node_pointer(folded_value: FoldedValue, line: u32) -> NodePointer { + debug_assert!(FoldedValue::valid_bus_node_pointer(&folded_value), "Caused by call at {}", line); + folded_value.bus_node_pointer.unwrap() +} +fn safe_unwrap_to_bus_slice(folded_value: FoldedValue, line: u32) -> (String, BusSlice) { + debug_assert!(FoldedValue::valid_arithmetic_slice(&folded_value), "Caused by call at {}", line); + folded_value.bus_slice.unwrap() +} fn safe_unwrap_to_single(slice: MemorySlice, line: u32) -> C { debug_assert!(slice.is_single(), "Caused by call at {}", line); MemorySlice::unwrap_to_single(slice) @@ -2285,10 +3567,20 @@ fn treat_result_with_memory_error_void( match memory_error { Result::Ok(()) => Result::Ok(()), Result::Err(MemoryError::MismatchedDimensionsWeak(dim_given, dim_original)) => { - let report = Report::warning( - format!("Typing warning: Mismatched dimensions, assigning to an array an expression of smaller length, the remaining positions are not modified. Initially all variables are initialized to 0.\n Expected length: {}, given {}", - dim_original, dim_given), - RuntimeError); + + let report = if dim_given < dim_original{ + Report::warning( + format!("Typing warning: Mismatched dimensions, assigning to an array an expression of smaller length, the remaining positions are not modified. Initially all variables are initialized to 0.\n Expected length: {}, given {}", + dim_original, dim_given), + RuntimeError + ) + } else{ + Report::warning( + format!("Typing warning: Mismatched dimensions, assigning to an array an expression of greater length, the remaining positions of the expression are not assigned to the array.\n Expected length: {}, given {}", + dim_original, dim_given), + RuntimeError + ) + }; add_report_to_runtime(report, meta, runtime_errors, call_trace); Ok(()) }, @@ -2315,13 +3607,28 @@ fn treat_result_with_memory_error_void( TypeInvalidAccess::NoInitializedSignal =>{ Report::error("Exception caused by invalid access: trying to access to a signal that is not initialized" .to_string(), RuntimeError) + }, + TypeInvalidAccess::NoInitializedBus =>{ + Report::error("Exception caused by invalid access: trying to access to a bus whose fields have not been completely initialized" .to_string(), + RuntimeError) } } } MemoryError::AssignmentError(type_asig_error) => { match type_asig_error{ + TypeAssignmentError::MultipleAssignmentsComponent =>{ + Report::error( + format!("Exception caused by invalid assignment\n The component has been assigned previously"), + RuntimeError) + }, + TypeAssignmentError::MultipleAssignmentsBus =>{ + Report::error( + format!("Exception caused by invalid assignment\n Bus contains fields that have been previously initialized"), + RuntimeError) + }, TypeAssignmentError::MultipleAssignments =>{ - Report::error("Exception caused by invalid assignment: signal already assigned".to_string(), + Report::error( + format!("Exception caused by invalid assignment\n Signal has been already assigned"), RuntimeError) }, TypeAssignmentError::AssignmentInput(signal) => Report::error( @@ -2336,6 +3643,10 @@ fn treat_result_with_memory_error_void( TypeAssignmentError::NoInitializedComponent =>{ Report::error("Exception caused by invalid assignment: trying to assign a value to a signal of a component that has not been initialized".to_string(), RuntimeError) + }, + TypeAssignmentError::DifferentBusInstances =>{ + Report::error("Exception caused by invalid assignment: trying to assign a different instance of the bus. The instances of the buses should be equal".to_string(), + RuntimeError) } } }, @@ -2348,6 +3659,11 @@ fn treat_result_with_memory_error_void( orig, given), RuntimeError) }, + MemoryError::MismatchedInstances => { + Report::error( + format!("Typing error found: mismatched instances.\n Trying to compare two different instances of a bus, the instances must be equal"), + RuntimeError) + }, MemoryError::UnknownSizeDimension => { Report::error("Array dimension with unknown size".to_string(), RuntimeError) @@ -2391,7 +3707,7 @@ fn treat_result_with_memory_error_void( } } -fn treat_result_with_memory_error( +pub fn treat_result_with_memory_error( memory_error: Result, meta: &Meta, runtime_errors: &mut ReportCollection, @@ -2424,12 +3740,27 @@ fn treat_result_with_memory_error( Report::error("Exception caused by invalid access: trying to access to a signal that is not initialized" .to_string(), RuntimeError) } + TypeInvalidAccess::NoInitializedBus =>{ + Report::error("Exception caused by invalid access: trying to access to a bus whose fields have not been completely initialized" .to_string(), + RuntimeError) + } } }, MemoryError::AssignmentError(type_asig_error) => { match type_asig_error{ + TypeAssignmentError::MultipleAssignmentsComponent =>{ + Report::error( + format!("Exception caused by invalid assignment\n The component has been assigned previously"), + RuntimeError) + }, + TypeAssignmentError::MultipleAssignmentsBus =>{ + Report::error( + format!("Exception caused by invalid assignment\n Bus contains fields that have been previously initialized"), + RuntimeError) + }, TypeAssignmentError::MultipleAssignments =>{ - Report::error("Exception caused by invalid assignment: signal already assigned".to_string(), + Report::error( + format!("Exception caused by invalid assignment\n Signal has been already assigned"), RuntimeError) }, TypeAssignmentError::AssignmentInput(signal) => Report::error( @@ -2444,6 +3775,10 @@ fn treat_result_with_memory_error( TypeAssignmentError::NoInitializedComponent =>{ Report::error("Exception caused by invalid assignment: trying to assign a value to a signal of a component that has not been initialized".to_string(), RuntimeError) + }, + TypeAssignmentError::DifferentBusInstances =>{ + Report::error("Exception caused by invalid assignment: trying to assign a different instance of the bus. The instances of the buses should be equal".to_string(), + RuntimeError) } } }, @@ -2478,6 +3813,11 @@ fn treat_result_with_memory_error( orig, given), RuntimeError) }, + MemoryError::MismatchedInstances => { + Report::error( + format!("Typing error found: mismatched instances.\n Trying to compare two different instances of a bus, the instances must be equal"), + RuntimeError) + }, MemoryError::UnknownSizeDimension => { Report::error("Array dimension with unknown size".to_string(), RuntimeError) } diff --git a/constraint_generation/src/execution_data/executed_bus.rs b/constraint_generation/src/execution_data/executed_bus.rs new file mode 100644 index 000000000..ac1a12523 --- /dev/null +++ b/constraint_generation/src/execution_data/executed_bus.rs @@ -0,0 +1,184 @@ +use super::type_definitions::*; + +use num_bigint::BigInt; +use std::collections::{HashMap, BTreeMap}; +use crate::execution_data::TagInfo; +use compiler::hir::very_concrete_program::*; + + + +pub struct BusConnexion{ + pub full_name: String, + pub inspect: BusData, + pub dag_offset: usize, + pub dag_jump: usize, +} + + +pub struct ExecutedBus { + pub bus_name: String, + pub report_name: String, + pub fields: WireCollector, + pub parameter_instances: ParameterContext, + pub signal_to_tags: TagContext, + pub bus_connexions: HashMap, + pub size: usize, + pub bus_id: Option, +} + +impl ExecutedBus { + pub fn new( + name: String, + report_name: String, + instance: ParameterContext, + ) -> ExecutedBus { + ExecutedBus { + report_name, + bus_name: name, + parameter_instances: instance, + fields: Vec::new(), + signal_to_tags: TagContext::new(), + bus_connexions: HashMap::new(), + size: 0, + bus_id: None, + } + } + + pub fn is_equal(&self, name: &str, context: &ParameterContext) -> bool { + self.bus_name == name + && self.parameter_instances == *context + } + + pub fn add_bus_arrow(&mut self, component_name: String, data: BusData) { + + let mut dimensions = &vec![]; + for wire_data in &self.fields{ + if *wire_data.name == component_name{ + dimensions = &wire_data.length; + } + } + let mut total_size = data.size; + for v in dimensions{ + total_size *= v; + } + self.size += total_size; + + let cnn = + BusConnexion { full_name: component_name.clone(), inspect: data, dag_offset: 0, dag_jump: 0}; + self.bus_connexions.insert(component_name, cnn); + } + + pub fn add_signal(&mut self, signal_name: &str, dimensions: &[usize]) { + let info_signal = WireData{ + name: signal_name.to_string(), + length: dimensions.to_vec(), + is_bus: false + }; + self.fields.push(info_signal); + let mut total_size = 1; + for v in dimensions{ + total_size *= v; + } + self.size += total_size; + } + + pub fn add_bus(&mut self, bus_name: &str, dimensions: &[usize]) { + let info_bus = WireData{ + name: bus_name.to_string(), + length: dimensions.to_vec(), + is_bus: true + }; + self.fields.push(info_bus); + } + + pub fn add_tag_signal(&mut self, signal_name: &str, tag_name: &str, value: Option){ + let tags_signal = self.signal_to_tags.get_mut(signal_name); + if tags_signal.is_none(){ + let mut new_tags_signal = TagInfo::new(); + new_tags_signal.insert(tag_name.to_string(), value); + self.signal_to_tags.insert(signal_name.to_string(), new_tags_signal); + } else { + tags_signal.unwrap().insert(tag_name.to_string(), value); + } + } + + pub fn bus_name(&self) -> &String { + &self.bus_name + } + + pub fn parameter_instances(&self) -> &ParameterContext { + &self.parameter_instances + } + + pub fn fields(&self) -> &WireCollector { + &self.fields + } + + pub fn bus_connexions(&self) -> &HashMap{ + &self.bus_connexions + } + + pub fn build_bus_info( + &self, + bus_id: usize, + bus_table: &mut Vec>, + buses_info: &Vec + ){ + if bus_table[bus_id].is_none(){ + let mut total_size = 0; + let mut offset = 0; + let mut wires = BTreeMap::new(); + let mut field_id = 0; + for info_field in &self.fields{ + let (name, lengths) = (&info_field.name, &info_field.length); + if !info_field.is_bus{ + // Case signal + let size = lengths.iter().fold(1, |p, c| p * (*c)); + let signal = FieldInfo { + field_id, + dimensions: lengths.clone(), + size, + offset, + bus_id: None + }; + wires.insert(name.clone(), signal); + total_size += size; + offset += size; + field_id += 1; + + } else{ + let bus_node = self.bus_connexions.get(name).unwrap().inspect.goes_to; + if bus_table[bus_node].is_none(){ + let exe_bus = buses_info.get(bus_node).unwrap(); + exe_bus.build_bus_info(bus_node, bus_table, buses_info); + } + let bus_instance = bus_table.get(bus_node).unwrap().as_ref().unwrap(); + + let size = lengths.iter().fold(bus_instance.size, |p, c| p * (*c)); + let bus = FieldInfo { + field_id, + dimensions: lengths.clone(), + size, + offset, + bus_id: Some(bus_node) + }; + wires.insert(name.clone(), bus); + total_size += size; + offset += size; + field_id += 1; + + } + } + bus_table[bus_id] = Some( + BusInstance{ + name: self.bus_name.clone(), + size: total_size, + fields: wires + } + ) + + } + } + + +} \ No newline at end of file diff --git a/constraint_generation/src/execution_data/executed_program.rs b/constraint_generation/src/execution_data/executed_program.rs index dcb1ce2da..f9ed7059a 100644 --- a/constraint_generation/src/execution_data/executed_program.rs +++ b/constraint_generation/src/execution_data/executed_program.rs @@ -1,6 +1,8 @@ use super::analysis::Analysis; use crate::FlagsExecution; use super::executed_template::{ExecutedTemplate, PreExecutedTemplate}; +use super::executed_bus::ExecutedBus; + use super::type_definitions::*; use compiler::hir::very_concrete_program::{Stats, VCPConfig, VCP}; use dag::DAG; @@ -14,7 +16,9 @@ pub type ExportResult = Result<(DAG, VCP, ReportCollection), ReportCollection>; pub struct ExecutedProgram { pub model: Vec, pub model_pretemplates: Vec, + pub model_buses: Vec, pub template_to_nodes: HashMap>, + pub bus_to_nodes: HashMap>, pub prime: String, } @@ -25,6 +29,8 @@ impl ExecutedProgram { template_to_nodes: HashMap::new(), prime: prime.clone(), model_pretemplates: Vec::new(), + model_buses: Vec::new(), + bus_to_nodes: HashMap::new(), } } @@ -41,6 +47,20 @@ impl ExecutedProgram { } Option::None } + pub fn identify_bus_node(&self, name: &str, context: &ParameterContext) -> Option { + if !self.bus_to_nodes.contains_key(name) { + return Option::None; + } + let related_nodes = self.bus_to_nodes.get(name).unwrap(); + for index in related_nodes { + let existing_node = &self.model_buses[*index]; + if ExecutedBus::is_equal(existing_node, name, context) { + return Option::Some(*index); + } + } + Option::None + } + pub fn number_of_nodes(&self) -> usize { self.model.len() } @@ -64,6 +84,12 @@ impl ExecutedProgram { } Option::Some(self.model_pretemplates[node_pointer].clone()) } + pub fn get_bus_node(&self, node_pointer: NodePointer) -> Option<&ExecutedBus> { + if node_pointer >= self.model_buses.len() { + return Option::None; + } + Option::Some(&self.model_buses[node_pointer]) + } pub fn add_prenode_to_scheme( &mut self, @@ -101,6 +127,32 @@ impl ExecutedProgram { node_index } + + pub fn add_bus_node_to_scheme( + &mut self, + node: ExecutedBus, + _analysis: Analysis, // not needed? + ) -> NodePointer { + //use super::filters::*; + // Clean code??? + //apply_unused(&mut node.code, &analysis, &self.prime); + //apply_computed(&mut node.code, &analysis); + // Insert template + let possible_index = self.identify_bus_node( + node.bus_name(), + node.parameter_instances(), + ); + if let Option::Some(index) = possible_index { + return index; + } + self.bus_to_nodes.entry(node.bus_name().clone()).or_insert_with(|| vec![]); + let nodes_for_bus = self.bus_to_nodes.get_mut(node.bus_name()).unwrap(); + let node_index = self.model_buses.len(); + self.model_buses.push(node); + nodes_for_bus.push(node_index); + node_index + } + pub fn export(mut self, mut program: ProgramArchive, flags: FlagsExecution) -> ExportResult { use super::executed_template::templates_in_mixed_arrays; fn merge_mixed(org: Vec, new: Vec) -> Vec { @@ -124,11 +176,24 @@ impl ExecutedProgram { } for exe in &mut self.model { - exe.insert_in_dag(&mut dag); + exe.insert_in_dag(&mut dag, &self.model_buses); + } + + let mut wrapped_buses_table = vec![None; self.model_buses.len()]; + let mut index = 0; + for exe_bus in &self.model_buses{ + exe_bus.build_bus_info(index, &mut wrapped_buses_table, &self.model_buses); + index += 1; + } + + let mut buses_table = Vec::new(); + for info in wrapped_buses_table{ + buses_table.push(info.unwrap()); } + for exe in self.model { - let tmp_instance = exe.export_to_circuit(&mut temp_instances); + let tmp_instance = exe.export_to_circuit(&mut temp_instances, &buses_table); temp_instances.push(tmp_instance); } @@ -158,6 +223,7 @@ impl ExecutedProgram { templates_in_mixed: mixed, program, prime: self.prime, + buses: buses_table }; let vcp = VCP::new(config); Result::Ok((dag, vcp, warnings)) diff --git a/constraint_generation/src/execution_data/executed_template.rs b/constraint_generation/src/execution_data/executed_template.rs index a56fc328a..d4e6651b0 100644 --- a/constraint_generation/src/execution_data/executed_template.rs +++ b/constraint_generation/src/execution_data/executed_template.rs @@ -1,4 +1,6 @@ +use super::executed_bus::BusConnexion; use super::type_definitions::*; +use super::ExecutedBus; use circom_algebra::algebra::ArithmeticExpression; use compiler::hir::very_concrete_program::*; use dag::DAG; @@ -64,10 +66,10 @@ pub struct ExecutedTemplate { pub code: Statement, pub template_name: String, pub report_name: String, - pub inputs: SignalCollector, - pub outputs: SignalCollector, - pub intermediates: SignalCollector, - pub ordered_signals: Vec, + pub inputs: WireCollector, + pub outputs: WireCollector, + pub intermediates: WireCollector, + pub ordered_signals: WireCollector, pub constraints: Vec, pub components: ComponentCollector, pub number_of_components: usize, @@ -80,6 +82,7 @@ pub struct ExecutedTemplate { pub is_custom_gate: bool, pub underscored_signals: Vec, connexions: Vec, + pub bus_connexions: HashMap, } impl ExecutedTemplate { @@ -105,14 +108,15 @@ impl ExecutedTemplate { parameter_instances: instance, signal_to_tags: tag_instances.clone(), tag_instances, - inputs: SignalCollector::new(), - outputs: SignalCollector::new(), - intermediates: SignalCollector::new(), - ordered_signals: Vec::new(), + inputs: WireCollector::new(), + outputs: WireCollector::new(), + intermediates: WireCollector::new(), + ordered_signals: WireCollector::new(), constraints: Vec::new(), components: ComponentCollector::new(), number_of_components: 0, connexions: Vec::new(), + bus_connexions: HashMap::new(), underscored_signals: Vec::new(), } } @@ -129,37 +133,40 @@ impl ExecutedTemplate { self.connexions.push(cnn); } - pub fn add_input(&mut self, input_name: &str, dimensions: &[usize]) { - self.inputs.push((input_name.to_string(), dimensions.to_vec())); + pub fn add_bus_arrow(&mut self, bus_name: String, data: BusData){ + let cnn = + BusConnexion { full_name:bus_name.clone(), inspect: data, dag_offset: 0, dag_jump: 0}; + self.bus_connexions.insert(bus_name, cnn); } - pub fn add_output(&mut self, output_name: &str, dimensions: &[usize]) { - self.outputs.push((output_name.to_string(), dimensions.to_vec())); + pub fn add_input(&mut self, input_name: &str, dimensions: &[usize], is_bus: bool) { + let wire_info = WireData{ + name: input_name.to_string(), + length: dimensions.to_vec(), + is_bus + }; + self.inputs.push(wire_info.clone()); + self.ordered_signals.push(wire_info); } - pub fn add_intermediate(&mut self, intermediate_name: &str, dimensions: &[usize]) { - self.intermediates.push((intermediate_name.to_string(), dimensions.to_vec())); + pub fn add_output(&mut self, output_name: &str, dimensions: &[usize], is_bus: bool) { + let wire_info = WireData{ + name: output_name.to_string(), + length: dimensions.to_vec(), + is_bus + }; + self.outputs.push(wire_info.clone()); + self.ordered_signals.push(wire_info); } - pub fn add_ordered_signal(&mut self, signal_name: &str, dimensions: &[usize]) { - fn generate_symbols(name: String, current: usize, dimensions: &[usize]) -> Vec { - let symbol_name = name.clone(); - if current == dimensions.len() { - vec![name] - } else { - let mut generated_symbols = vec![]; - let mut index = 0; - while index < dimensions[current] { - let new_name = format!("{}[{}]", symbol_name, index); - generated_symbols.append(&mut generate_symbols(new_name, current + 1, dimensions)); - index += 1; - } - generated_symbols - } - } - for signal in generate_symbols(signal_name.to_string(), 0, dimensions) { - self.ordered_signals.push(signal); - } + pub fn add_intermediate(&mut self, intermediate_name: &str, dimensions: &[usize], is_bus: bool) { + let wire_info = WireData{ + name: intermediate_name.to_string(), + length: dimensions.to_vec(), + is_bus + }; + self.intermediates.push(wire_info.clone()); + self.ordered_signals.push(wire_info); } pub fn add_tag_signal(&mut self, signal_name: &str, tag_name: &str, value: Option){ @@ -198,19 +205,19 @@ impl ExecutedTemplate { &self.tag_instances } - pub fn inputs(&self) -> &SignalCollector { + pub fn inputs(&self) -> &WireCollector { &self.inputs } - pub fn outputs(&self) -> &SignalCollector { + pub fn outputs(&self) -> &WireCollector { &self.outputs } - pub fn intermediates(&self) -> &SignalCollector { + pub fn intermediates(&self) -> &WireCollector { &self.intermediates } - pub fn insert_in_dag(&mut self, dag: &mut DAG) { + pub fn insert_in_dag(&mut self, dag: &mut DAG, buses_info : &Vec) { let parameters = { let mut parameters = vec![]; for (_, data) in self.parameter_instances.clone() { @@ -225,41 +232,70 @@ impl ExecutedTemplate { dag.add_node( self.report_name.clone(), parameters, - self.ordered_signals.clone(), // pensar si calcularlo en este momento para no hacer clone self.is_parallel, self.is_custom_gate ); - self.build_signals(dag); + self.build_wires(dag, buses_info); + self.build_ordered_signals(dag, buses_info); self.build_connexions(dag); self.build_constraints(dag); } - fn build_signals(&self, dag: &mut DAG) { - for (name, dim) in self.outputs() { - let state = State { name: name.clone(), dim: 0 }; - let config = SignalConfig { signal_type: 1, dimensions: dim, is_public: false }; - generate_symbols(dag, state, &config); - } - for (name, dim) in self.inputs() { - if self.public_inputs.contains(name) { - let state = State { name: name.clone(), dim: 0 }; - let config = SignalConfig { signal_type: 0, dimensions: dim, is_public: true }; + fn build_wires(&self, dag: &mut DAG, buses_info : &Vec) { + for wire_data in self.outputs() { + let state = State { basic_name: wire_data.name.clone(), name: wire_data.name.clone(), dim: 0 }; + let config = SignalConfig { signal_type: 1, dimensions: &wire_data.length, is_public: false }; + if wire_data.is_bus{ + generate_bus_symbols(dag, state, &config, &self.bus_connexions, buses_info ); + } else{ generate_symbols(dag, state, &config); } } - for (name, dim) in self.inputs() { - if !self.public_inputs.contains(name) { - let state = State { name: name.clone(), dim: 0 }; - let config = SignalConfig { signal_type: 0, dimensions: dim, is_public: false }; + for wire_data in self.inputs() { + if self.public_inputs.contains(&wire_data.name) { + let state = State { basic_name: wire_data.name.clone(), name: wire_data.name.clone(), dim: 0 }; + let config = SignalConfig { signal_type: 0, dimensions: &wire_data.length, is_public: true }; + if wire_data.is_bus{ + generate_bus_symbols(dag, state, &config, &self.bus_connexions, buses_info ); + } else{ + generate_symbols(dag, state, &config); + } + } + } + for wire_data in self.inputs() { + if !self.public_inputs.contains(&wire_data.name) { + let state = State { basic_name: wire_data.name.clone(), name: wire_data.name.clone(), dim: 0 }; + let config = SignalConfig { signal_type: 0, dimensions: &wire_data.length, is_public: false }; + if wire_data.is_bus{ + generate_bus_symbols(dag, state, &config, &self.bus_connexions, buses_info ); + } else{ + generate_symbols(dag, state, &config); + } + } + } + for wire_data in self.intermediates() { + let state = State { basic_name: wire_data.name.clone(), name: wire_data.name.clone(), dim: 0 }; + let config = SignalConfig { signal_type: 2, dimensions: &wire_data.length, is_public: false }; + if wire_data.is_bus{ + generate_bus_symbols(dag, state, &config, &self.bus_connexions, buses_info ); + } else{ generate_symbols(dag, state, &config); } } - for (name, dim) in self.intermediates() { - let state = State { name: name.clone(), dim: 0 }; - let config = SignalConfig { signal_type: 2, dimensions: dim, is_public: false }; - generate_symbols(dag, state, &config); + } + + fn build_ordered_signals(&self, dag: &mut DAG, buses_info : &Vec) { + for wire_data in &self.ordered_signals { + let state = State { basic_name: wire_data.name.clone(), name: wire_data.name.clone(), dim: 0 }; + let config = OrderedSignalConfig { dimensions: &wire_data.length }; + if wire_data.is_bus{ + generate_ordered_bus_symbols(dag, state, &config, &self.bus_connexions, buses_info ); + } else{ + generate_ordered_symbols(dag, state, &config); + } } } + fn build_connexions(&mut self, dag: &mut DAG) { self.connexions.sort_by(|l, r| { use std::cmp::Ordering; @@ -298,7 +334,7 @@ impl ExecutedTemplate { } } - pub fn export_to_circuit(self, instances: &mut [TemplateInstance]) -> TemplateInstance { + pub fn export_to_circuit(self, instances: &mut [TemplateInstance], buses_info : &Vec) -> TemplateInstance { use SignalType::*; fn build_triggers( instances: &mut [TemplateInstance], @@ -309,6 +345,14 @@ impl ExecutedTemplate { let data = cnn.inspect; instances[data.goes_to].is_parallel_component |= data.is_parallel; instances[data.goes_to].is_not_parallel_component |= !(data.is_parallel); + + let mut external_wires = Vec::new(); + for wire in &instances[data.goes_to].wires{ + if wire.xtype() != SignalType::Intermediate{ + external_wires.push(wire.clone()); + } + } + let trigger = Trigger { offset: cnn.dag_offset, component_offset: cnn.dag_component_offset, @@ -317,7 +361,7 @@ impl ExecutedTemplate { is_parallel: data.is_parallel || instances[data.goes_to].is_parallel, runs: instances[data.goes_to].template_header.clone(), template_id: data.goes_to, - external_signals: instances[data.goes_to].signals.clone(), + external_wires, has_inputs: instances[data.goes_to].number_of_inputs > 0, }; triggers.push(trigger); @@ -368,7 +412,7 @@ impl ExecutedTemplate { let mut public = vec![]; let mut not_public = vec![]; for s in self.inputs { - if self.public_inputs.contains(&s.0) { + if self.public_inputs.contains(&s.name) { public.push(s); } else { not_public.push(s); @@ -376,33 +420,111 @@ impl ExecutedTemplate { } let mut local_id = 0; let mut dag_local_id = 1; - for (name, lengths) in self.outputs { - let signal = Signal { name, lengths, local_id, dag_local_id, xtype: Output}; - local_id += signal.size(); - dag_local_id += signal.size(); - instance.add_signal(signal); + for s in self.outputs { + if s.is_bus{ + let bus_node = self.bus_connexions.get(&s.name).unwrap().inspect.goes_to; + let info_bus = buses_info.get(bus_node).unwrap(); + let size = s.length.iter().fold(info_bus.size, |p, c| p * (*c)); + let bus = Bus{ + name: s.name, + lengths: s.length, + local_id, + dag_local_id, + bus_id: bus_node, + size, + xtype: Output, + }; + local_id += bus.size; + dag_local_id += bus.size; + instance.add_signal(Wire::TBus(bus)); + } else{ + let size = s.length.iter().fold(1, |p, c| p * (*c)); + let signal = Signal { name: s.name, lengths: s.length, local_id, dag_local_id, xtype: Output, size}; + local_id += signal.size; + dag_local_id += signal.size; + instance.add_signal(Wire::TSignal(signal)); + } } - for (name, lengths) in public { - let signal = Signal { name, lengths, local_id, dag_local_id, xtype: Input}; - local_id += signal.size(); - dag_local_id += signal.size(); - instance.add_signal(signal); + + for s in public { + if s.is_bus{ + let bus_node = self.bus_connexions.get(&s.name).unwrap().inspect.goes_to; + let info_bus = buses_info.get(bus_node).unwrap(); + let size = s.length.iter().fold(info_bus.size, |p, c| p * (*c)); + let bus = Bus{ + name: s.name, + lengths: s.length, + local_id, + dag_local_id, + bus_id: bus_node, + size, + xtype: Input, + }; + local_id += bus.size; + dag_local_id += bus.size; + instance.add_signal(Wire::TBus(bus)); + } else{ + let size = s.length.iter().fold(1, |p, c| p * (*c)); + let signal = Signal { name: s.name, lengths: s.length, local_id, dag_local_id, xtype: Input, size}; + local_id += signal.size; + dag_local_id += signal.size; + instance.add_signal(Wire::TSignal(signal)); + } } - for (name, lengths) in not_public { - let signal = Signal { name, lengths, local_id, dag_local_id, xtype: Input}; - local_id += signal.size(); - dag_local_id += signal.size(); - instance.add_signal(signal); + for s in not_public { + if s.is_bus{ + let bus_node = self.bus_connexions.get(&s.name).unwrap().inspect.goes_to; + let info_bus = buses_info.get(bus_node).unwrap(); + let size = s.length.iter().fold(info_bus.size, |p, c| p * (*c)); + let bus = Bus{ + name: s.name, + lengths: s.length, + local_id, + dag_local_id, + bus_id: bus_node, + size, + xtype: Input, + }; + local_id += bus.size; + dag_local_id += bus.size; + instance.add_signal(Wire::TBus(bus)); + } else{ + let size = s.length.iter().fold(1, |p, c| p * (*c)); + let signal = Signal { name: s.name, lengths: s.length, local_id, dag_local_id, xtype: Input, size}; + local_id += signal.size; + dag_local_id += signal.size; + instance.add_signal(Wire::TSignal(signal)); + } } - for (name, lengths) in self.intermediates { - let signal = Signal { name, lengths, local_id, dag_local_id, xtype: Intermediate}; - local_id += signal.size(); - dag_local_id += signal.size(); - instance.add_signal(signal); + for s in self.intermediates { + if s.is_bus{ + let bus_node = self.bus_connexions.get(&s.name).unwrap().inspect.goes_to; + let info_bus = buses_info.get(bus_node).unwrap(); + let size = s.length.iter().fold(info_bus.size, |p, c| p * (*c)); + let bus = Bus{ + name: s.name, + lengths: s.length, + local_id, + dag_local_id, + bus_id: bus_node, + size, + xtype: Intermediate, + }; + local_id += bus.size; + dag_local_id += bus.size; + instance.add_signal(Wire::TBus(bus)); + } else{ + let size = s.length.iter().fold(1, |p, c| p * (*c)); + let signal = Signal { name: s.name, lengths: s.length, local_id, dag_local_id, xtype: Intermediate, size}; + local_id += signal.size; + dag_local_id += signal.size; + instance.add_signal(Wire::TSignal(signal)); + } } instance } + } struct SignalConfig<'a> { @@ -411,7 +533,8 @@ struct SignalConfig<'a> { dimensions: &'a [usize], } struct State { - name: String, + basic_name: String, //Only name without array accesses []. + name: String, //Full name with array accesses. dim: usize, } fn generate_symbols(dag: &mut DAG, state: State, config: &SignalConfig) { @@ -427,13 +550,85 @@ fn generate_symbols(dag: &mut DAG, state: State, config: &SignalConfig) { let mut index = 0; while index < config.dimensions[state.dim] { let new_state = - State { name: format!("{}[{}]", state.name, index), dim: state.dim + 1 }; + State { basic_name: state.basic_name.clone(), name: format!("{}[{}]", state.name, index), dim: state.dim + 1 }; generate_symbols(dag, new_state, config); index += 1; } } } +// TODO: move to bus? +fn generate_bus_symbols(dag: &mut DAG, state: State, config: &SignalConfig, bus_connexions: &HashMap, buses: &Vec) { + let bus_connection = bus_connexions.get(&state.basic_name).unwrap(); + let ex_bus2 = buses.get(bus_connection.inspect.goes_to).unwrap(); + if state.dim == config.dimensions.len() { + for info_field in ex_bus2.fields(){ + let signal_name = format!("{}.{}",state.name, info_field.name); + let state = State { basic_name: info_field.name.clone(), name: signal_name, dim: 0 }; + let config = SignalConfig { signal_type: config.signal_type, dimensions: &info_field.length, is_public: config.is_public }; + if info_field.is_bus{ + generate_bus_symbols(dag, state, &config, ex_bus2.bus_connexions(), buses); + } else{ + generate_symbols(dag, state, &config); + } + } + + } else { + let mut index = 0; + while index < config.dimensions[state.dim] { + let new_state = + State { basic_name: state.basic_name.clone(), name: format!("{}[{}]", state.name, index), dim: state.dim + 1 }; + generate_bus_symbols(dag, new_state, config, bus_connexions, buses); + index += 1; + } + } +} + + +struct OrderedSignalConfig<'a> { + dimensions: &'a [usize], +} +fn generate_ordered_symbols(dag: &mut DAG, state: State, config: &OrderedSignalConfig) { + if state.dim == config.dimensions.len() { + dag.add_ordered_signal(state.name); + } else { + let mut index = 0; + while index < config.dimensions[state.dim] { + let new_state = + State { basic_name: state.basic_name.clone(), name: format!("{}[{}]", state.name, index), dim: state.dim + 1 }; + generate_ordered_symbols(dag, new_state, config); + index += 1; + } + } +} + +// TODO: move to bus? +fn generate_ordered_bus_symbols(dag: &mut DAG, state: State, config: &OrderedSignalConfig, bus_connexions: &HashMap, buses: &Vec) { + let bus_connection = bus_connexions.get(&state.basic_name).unwrap(); + let ex_bus2 = buses.get(bus_connection.inspect.goes_to).unwrap(); + if state.dim == config.dimensions.len() { + for info_field in ex_bus2.fields(){ + let signal_name = format!("{}.{}",state.name, info_field.name); + let state = State { basic_name: info_field.name.clone(), name: signal_name, dim: 0 }; + let config = OrderedSignalConfig {dimensions: &info_field.length }; + if info_field.is_bus{ + generate_ordered_bus_symbols(dag, state, &config, ex_bus2.bus_connexions(), buses); + } else{ + generate_ordered_symbols(dag, state, &config); + } + } + + } else { + let mut index = 0; + while index < config.dimensions[state.dim] { + let new_state = + State { basic_name: state.basic_name.clone(), name: format!("{}[{}]", state.name, index), dim: state.dim + 1 }; + generate_ordered_bus_symbols(dag, new_state, config, bus_connexions, buses); + index += 1; + } + } +} + fn as_big_int(exprs: Vec>) -> Vec { let mut numbers = Vec::with_capacity(exprs.len()); for e in exprs { diff --git a/constraint_generation/src/execution_data/filters.rs b/constraint_generation/src/execution_data/filters.rs index 20b080ab4..31a3fd1bb 100644 --- a/constraint_generation/src/execution_data/filters.rs +++ b/constraint_generation/src/execution_data/filters.rs @@ -189,6 +189,9 @@ fn apply_computed_expr(expr: &mut Expression, analysis: &Analysis) { *rhe = Box::new(computed_or_original(analysis, rhe)); apply_computed_expr(rhe, analysis); } + BusCall{args, ..} =>{ + apply_computed_expr_vec(args, analysis); + } _ => {unreachable!("Anonymous calls should not be reachable at this point."); } } } diff --git a/constraint_generation/src/execution_data/mod.rs b/constraint_generation/src/execution_data/mod.rs index 6dcc6c012..b471a0035 100644 --- a/constraint_generation/src/execution_data/mod.rs +++ b/constraint_generation/src/execution_data/mod.rs @@ -2,10 +2,12 @@ use super::environment_utils::slice_types::{AExpressionSlice, TagInfo}; use circom_algebra::algebra::Constraint; pub use executed_program::ExecutedProgram; pub use executed_template::{PreExecutedTemplate, ExecutedTemplate}; +pub use executed_bus::ExecutedBus; pub use type_definitions::NodePointer; pub mod analysis; pub mod executed_program; pub mod executed_template; +pub mod executed_bus; mod filters; pub mod type_definitions; diff --git a/constraint_generation/src/execution_data/type_definitions.rs b/constraint_generation/src/execution_data/type_definitions.rs index 03ef731c3..5086ea02d 100644 --- a/constraint_generation/src/execution_data/type_definitions.rs +++ b/constraint_generation/src/execution_data/type_definitions.rs @@ -9,8 +9,14 @@ pub type Constraint = ConstraintGen; pub type ParameterContext = BTreeMap; pub type TagContext = BTreeMap; pub type TagInfo = BTreeMap>; -// From name to dimensions -pub type SignalCollector = Vec<(String, Vec)>; +// From name to dimensions and if it is bus or not +#[derive(Clone)] +pub struct WireData{ + pub name: String, + pub length: Vec, + pub is_bus: bool +} +pub type WireCollector = Vec; pub type ComponentCollector = Vec<(String, Vec)>; pub struct SubComponentData { pub name: String, @@ -18,3 +24,45 @@ pub struct SubComponentData { pub indexed_with: Vec, pub goes_to: NodePointer, } + +pub struct BusData { + pub name: String, + pub goes_to: NodePointer, + pub size: usize, +} + +/* + Usable representation of a series of accesses performed over a symbol representing a bus. + AccessingInformationBus { + pub undefined: bool ===> true if one of the index values could not be transformed into a SliceCapacity during the process, + pub array_access: Vec + pub field_access: Option // may not appear + pub remaining_access: Option, // may not appear + } +*/ +#[derive(Clone)] +pub struct AccessingInformationBus { + pub undefined: bool, + pub array_access: Vec, + pub field_access: Option, + pub remaining_access: Option>, +} + + +/* + Usable representation of a series of accesses performed over a symbol. + AccessingInformation { + pub undefined: bool ===> true if one of the index values could not be transformed into a SliceCapacity during the process, + pub before_signal: Vec, + pub signal_access: Option ==> may not appear, + pub after_signal: Vec + pub tag_access: Option ==> may not appear, + } +*/ +pub struct AccessingInformation { + pub undefined: bool, + pub before_signal: Vec, + pub signal_access: Option, + pub after_signal: Vec, + pub tag_access: Option +} diff --git a/constraint_generation/src/lib.rs b/constraint_generation/src/lib.rs index a7d26eb2a..90eb2f556 100644 --- a/constraint_generation/src/lib.rs +++ b/constraint_generation/src/lib.rs @@ -5,6 +5,7 @@ mod compute_constants; mod environment_utils; mod execute; mod execution_data; +mod assignment_utils; use ansi_term::Colour; use circom_algebra::algebra::{ArithmeticError, ArithmeticExpression}; diff --git a/dag/Cargo.toml b/dag/Cargo.toml index 403523089..4896145d5 100644 --- a/dag/Cargo.toml +++ b/dag/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dag" -version = "2.1.8" +version = "2.2.0" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/dag/src/lib.rs b/dag/src/lib.rs index 8c51193c8..19d325da3 100644 --- a/dag/src/lib.rs +++ b/dag/src/lib.rs @@ -161,7 +161,6 @@ impl Node { id: usize, template_name: String, parameters: Vec, - ordered_signals: Vec, is_parallel: bool, is_custom_gate: bool ) -> Node { @@ -169,7 +168,6 @@ impl Node { template_name, entry: Edge::new_entry(id), parameters, number_of_components: 1, - ordered_signals, is_parallel, has_parallel_sub_cmp: false, is_custom_gate, @@ -215,6 +213,10 @@ impl Node { self.intermediates_length += 1; } + fn add_ordered_signal(&mut self, name: String){ + self.ordered_signals.push(name); + } + fn add_constraint(&mut self, constraint: Constraint) { self.constraints.push(constraint) } @@ -372,13 +374,12 @@ impl DAG { &mut self, template_name: String, parameters: Vec, - ordered_signals: Vec, is_parallel: bool, is_custom_gate: bool ) -> usize { let id = self.nodes.len(); self.nodes.push( - Node::new(id, template_name, parameters, ordered_signals, is_parallel, is_custom_gate) + Node::new(id, template_name, parameters, is_parallel, is_custom_gate) ); self.adjacency.push(vec![]); id @@ -402,6 +403,12 @@ impl DAG { } } + pub fn add_ordered_signal(&mut self, name: String) { + if let Option::Some(node) = self.get_mut_main() { + node.add_ordered_signal(name); + } + } + pub fn add_constraint(&mut self, constraint: Constraint) { if let Option::Some(node) = self.get_mut_main() { node.add_constraint(constraint); diff --git a/mkdocs/docs/circom-language/basic-operators.md b/mkdocs/docs/circom-language/basic-operators.md index 089615a22..c2940bc7f 100644 --- a/mkdocs/docs/circom-language/basic-operators.md +++ b/mkdocs/docs/circom-language/basic-operators.md @@ -95,8 +95,8 @@ All bitwise operators are performed modulo p. | :--- | :--- | :--- | | & | a & b | Bitwise AND | | \| | a \| b | Bitwise OR | -| ~ | ~a | Complement 254 bits | -| ^ | a ^ b | XOR 254 bits | +| ~ | ~a | Complement to the number of bits of the prime number | +| ^ | a ^ b | Bitwise XOR | | >> | a >> 4 | Right shift operator | | << | a << 4 | Left shift operator | @@ -105,9 +105,9 @@ All bitwise operators are performed modulo p. For all ```k``` with ```0=< k <= p/2``` (integer division) we have that * ```x >> k = x/(2**k)``` -* ```x << k = (x*(2{**}k)~ & ~mask) % p ``` +* ```x << k = (x*(2**k)~ & ~mask) % p ``` -where b is the number of significant bits of p and mask is ```2{**}b - 1```. +where b is the number of significant bits of p and mask is ```2**b - 1```. For all ```k``` with ```p/2 +1<= k < p``` we have that @@ -122,8 +122,8 @@ There are operators that combine bitwise operators with a final assignment. | :--- | :--- | :--- | | &= | a &= b | Bitwise AND and assignment | | \|= | a \|= b | Bitwise OR and assignment | -| ~= | ~=a | Complement 254 bits and assignment | -| ^= | a ^= b | XOR 254 bits and assignment | +| ~= | ~=a | Complement to the number of bits of the prime number and assignment | +| ^= | a ^= b | Bitwise XOR and assignment | | >>= | a >>= 4 | Right shift operator and assignment | | <<= | a <<= 4 | Left shift operator and assignment | diff --git a/mkdocs/docs/circom-language/buses.md b/mkdocs/docs/circom-language/buses.md new file mode 100644 index 000000000..1a25d9261 --- /dev/null +++ b/mkdocs/docs/circom-language/buses.md @@ -0,0 +1,250 @@ +# Buses +circom 2.2.0 introduces a new feature called __signal buses__. + + + +## Definition +A bus is a collection of different but related signals grouped under one name. They are similar to structs in programming languages like C++, helping to make the code more organized and easier to manage. + +Buses can be defined at the same level as templates and can be used as inputs, intermediates or ouputs within a template. + +``` +bus NameBus(param1,...,paramN){ + //signals, + //arrays, + //other buses... +} + +``` + +In many circuits we have pairs of signals `x` and `y`, which represent the two components of a point. With the new bus feature, we can define a `Point` bus as follows: + +``` +bus Point(){ + signal x; + signal y; +} +``` + +This way, it is clear that `x` and `y` should be understood as a single point rather than two independent signals. + +Using buses, we can modify many templates from the circomlib to make them more readable and organized. Let us consider the `Edwards2Montgomery` template from `montgomery.circom`: + +``` +template Edwards2Montgomery () { + input Point() { edwards_point } in ; + output Point() { montgomery_point } out ; + + out.x <–- (1 + in.y ) / (1 - in.y ) ; + out.y <–- out.x / in.x ; + + out.x * (1 - in.y ) === (1 + in.y ) ; + out.y * in.x === out.x ; + } +``` + +Here, we have a template with an input `Point` `in` expected to be in Edwards format, and an output `Point` `out` in Montgomery format. + +The power of buses lies in expressing properties about a collection of related signals. For example, the two signals inside the bus `in` (respectively `out`) must satisfy the equations for the Edwards curve (respectively the Montgomery curve). Before circom 2.2.0, this could not be expressed using tags in circom. +But now, we can tag each bus with the corresponding expected format. + +Besides tagging buses defined in a template, we can also tag their different fields. Let us see this feature in the following example: + +``` +bus Book () { + signal {maxvalue} title[50]; + signal {maxvalue} author[50]; + signal {maxvalue} sold_copies; + signal {maxvalue} year; +}; +``` + +The `Book` bus has four different fields: +signal arrays `title` and `author` whose letters have a maximum value, the number of sold copies `sold_copies`, and the publication `year`, which also has a maximum value. Using buses makes your code clearer and more readable. It is easier to understand that a `Book` bus represents a book with its fields, rather than dealing with individual signals. + +``` +template BestSeller2024(){ + input Book() book; + output Book() {best_seller2024} best_book; + signal check_copies <== LessThan(book.sold_copies.maxvalue)([1000000,book.sold_copies]); + check_copies === 1; + signal check_2024 <== IsEqual()([book.year,2024]); + check_2024 === 1; + best_book <== book; +} +``` + +As mentioned above, tags work at both levels: at the level of the whole bus, expressing that the book is a best-seller in 2024 (it sold more than 1 million copies), and at the level of the bus signals, expressing the different correctness properties about the book's fields. + +## Approaching a Type System via Buses and Tags +The introduction of buses in circom 2.2.0 brings us closer to having a robust type system. By enforcing compatibility rules in the bus assignments and enabling tagging at both the bus and signal level, buses provide a structured way to manage and verify the relationships between different signals. The combined use of buses and tags emulates the advantages of a traditional type system within circom, enhancing code clarity, reducing errors, and improving overall organization. + +When assigning one bus to another, they both need to be the same type of bus. Otherwise, the compiler reports an error. + +``` +bus B1(){ + signal x; +} + +bus B2() { + signal x; +} + +template B1toB2(){ + input B1() b1; + output B2() b2; + b2 <== b1; +} + +``` +For the previous example, the compiler reports: +``` +error[T2059]: Typing error found + ┌─ "example.circom":80:5 + │ + │ b2 <== b1; + │ ^^^^^^^^^ Assignee and assigned types do not match. +``` + +In this case, the transformation from one type to another should be explicitly done as follows: `b2.x <== b1.x;`. + +Consider again the `BestSeller2024` template and a possible instantiation: `Book seller <== BestSeller2024()(b);` Similar to tags, whenever a template is instantiated, the compiler checks if the type of `b` is equals to `Book`. If it is not, an error is reported. The compiler also checks if the bus' fields have the same tags. + +## Buses inside Buses +We can have buses inside the definition other buses, as long as we do not define buses recursively. To illustrate this, let us consider now, a new kind of bus, `Person`, which contains some information about a person: + +``` +bus Film() { + signal title[50]; + signal director[50]; + signal year; +} + +bus Date() { + signal day; + signal month; + signal year; +} + +bus Person() { + signal name[50]; + Film() films[10]; + Date() birthday; +} +``` + +## Parameterized Buses +Buses can have parameters as well. These parameters must be known during compilation time and can be used to define arrays or other buses inside themselves. + +Let us generalize the `Point` bus for a given dimension. + +``` +bus PointN(dim){ + signal x[dim]; +} +``` + +Thanks to this definition, we can define other like lines and figures. + +``` +bus Line(dim){ + PointN(dim) start; + PointN(dim) end; +} + +bus Figure(num_sides, dim){ + Line(dim) side[num_sides]; +} +``` +Notice that the `Figure` bus is defined by two parameters: the number of sides and the dimension of its points. Using this bus, we can define every kind of figure in a very simple way. For instance: + +``` +bus Triangle2D(){ + Figure(3,2) {well_defined} triangle; +} + +bus Square3D(){ + Figure(4,3) {well_defined} square; +} +``` + +We define a `Triangle2D` bus with three lines whose points are 2-dimensional, and a `Square3D` bus, whose points are 3-dimensional. + +``` +template well_defined_figure(num_sides, dimension){ + input Figure(num_sides,dimension) t; + output Figure(num_sides,dimension) {well_defined} correct_t; + var all_equals = 0; + var isequal = 0; + for(var i = 0; i < num_sides; i=i+1){ + for(var j = 0; j < dimension; j=j+1){ + isequal = IsEqual()([t.side[i].end.x[j],t.side[(i+1)%num_sides].start.x[j]]); + all_equals += isequal; + } + } + all_equals === num_sides; + correct_t <== t; +} +``` + +The previous template defines a correctness check for any figure: the ending point of a line must be the starting point of the next line. Otherwise, the figure is not well defined, and the witness generation will fail. + +## Buses as Circuit Inputs +Similar to signals, buses can be part of the main circuit's inputs. Therefore, we must specify their values to generate a witness for the circuit. For each circuit input bus, values can be specified in two ways: + +- __Serialized Format__: Indicate the value of every signal, bus, or array field in a single array, following the bus's definition order. +- __JSON Format__: Provide values using a fully qualified JSON format with field names. Note that you cannot mix both methods within a single bus. If you start defining an input using field names, you must use this method consistently throughout. + +Let us consider again the `Person` bus: +``` +bus Film() { + signal title[2]; + signal director[2]; + signal year; +} + +bus Date() { + signal day; + signal month; + signal year; +} + +bus Person() { + signal name[2]; + Film() films[2]; + Date() birthday; +} +``` + +To indicate values for an input `p` of this kind, we would indicate its values as one of the following ways: + +- __Serialized format__: +``` +{"p": ["80","82","20","21","30","31","1953","40","41","50","51","1990","1","1","1992"] +} +``` + + - __JSON format__: +``` +{"p": {"name": ["80","82"], + "films": [ + { "title": ["20","21"], + "director": ["30","31"], + "year": "1953" + }, + { "title": ["40","41"], + "director": ["50","51"], + "year": "1990" + } + ], + "birthday": + { "day": "1", + "month": "1", + "year": "1992" + } + } +} + +``` + +Like public input signals, public input buses cannot be tagged. Otherwise, the compiler will report an error. diff --git a/mkdocs/docs/circom-language/circom-insight/simplification.md b/mkdocs/docs/circom-language/circom-insight/simplification.md index 244025ccb..5b99baec7 100644 --- a/mkdocs/docs/circom-language/circom-insight/simplification.md +++ b/mkdocs/docs/circom-language/circom-insight/simplification.md @@ -1,8 +1,8 @@ # Constraint simplification -Constraint simplification is a key part of the `circom` compiler. Full simplification is activated by default, and its associated flag is `--O2` (see the [compilation options](../../getting-started/compilation-options.md)). Simplification is not applied when the flag `--O0` is activated, and a weaker (and faster) form of simplification is applied when using the flag `--O1`. +Constraint simplification is a key part of the `circom` compiler. A fast simplification `--O1` is activated by default (it only applies constant and renaming simplifications), and its associated flag is `--O1` (see the [compilation options](../../getting-started/compilation-options.md)). Simplification is not applied when the flag `--O0` is activated, and a full form of simplification is applied when using the flag `--O2`. -Let us explain the performed simplification in detail. +Let us explain the kind of simplification we can perform in detail. As pointed out in Section 2.3 (Quadratic arithmetic programs) of the [Groth16 paper](https://eprint.iacr.org/2016/260) (where ZK-SNARKs based on arithmetic circuits were introduced): @@ -16,7 +16,7 @@ In the context of [Groth16], the statement to be proved is that given the public In case we are using the PLONK proof system (instead of Groth16), since additions are not free we cannot remove linear constraints anymore. Still we can remove equalities between signals or equalities between signals and constants which is made with the flag --O1 (see below). Moreover, note that if we apply linear simplification to a constraint system in PLONK format, the resulting constraints will in general not be in PLONK format anymore, and transforming the result back to PLONK format may lead to a worse result than the original. For this reason, when using PLONK, it is always recommended to use the --O1 flag. -Once we have explained why removing any private signal (including the private inputs) and applying linear simplification is correct, let us explain what kind of simplification is applied when we enable the flag `--O1` or the flag `--O2` (which is activated by default). Notice that if we do not want to apply any simplification we must use the flag `--O0`. +Once we have explained why removing any private signal (including the private inputs) and applying linear simplification is correct, let us explain what kind of simplification is applied when we enable the flag `--O1` (which is activated by default) or the flag `--O2`. Notice that if we do not want to apply any simplification we must use the flag `--O0`. * Flag ```--O1``` removes two kinds of simple constraints: a) ```signal = K```, being K is a constant in $F_p$ and b) ```signal1 = signal2```. In both cases, at least one of the signals must be private, and it is the one that will be replaced by the other side. Note that there are usually many equalities between two signals in constraints defined by circom programs as they are many times used to connect components with their sub components. @@ -30,6 +30,8 @@ Only one of these flags/options can be enabled in the compilation. In case we want to see the simplification applied we can use the flag [```--simplification_substitution```](../../getting-started/compilation-options.md) to obtain a json file whose format is described [here](../formats/simplification-json.md). -Note that, although the full simplification applied `--O2` can significantly reduce the number of constraints and signals, which has a positive impact in the time and space needed to compute the proof, this is the most time and space consuming phase of the compilation process. Hence, with large circuits, say with millions of constraints, compilation can take a long time (even minutes or hours) and can run in out-of-memory exceptions. In such cases, it is recommended to only use the `--O2` flag in the final steps of the project development. +Since circom 2.2.0, we have set `--O1` as the default simplification option. This decision aligns with the growing use of Plonk, as `--O2` is not compatible with it. + +Note that, using the full simplification `--O2` can significantly reduce the number of constraints and signals, which has a positive impact in the time and space needed to compute the proof. However, this is the most time and space consuming phase of the compilation process. Hence, with large circuits, say with millions of constraints, compilation can take a long time (even minutes or hours) and can run in out-of-memory exceptions. In such cases, it is recommended to only use the `--O2` flag in the final steps of the project development. [Groth16] Jens Groth. "On the Size of Pairing-Based Non-interactive Arguments". Advances in Cryptology -- EUROCRYPT 2016, pages 305--326. Springer Berlin Heidelberg, 2016. diff --git a/mkdocs/docs/circom-language/circom-insight/unknowns.md b/mkdocs/docs/circom-language/circom-insight/unknowns.md index 38c1ba881..b982ab002 100644 --- a/mkdocs/docs/circom-language/circom-insight/unknowns.md +++ b/mkdocs/docs/circom-language/circom-insight/unknowns.md @@ -62,6 +62,39 @@ component main = A(); In the code above, an array is defined with an unknown size of value `in` (as signals are always considered unknown). +## Bus +A bus definition must be parametrized only by known values. + +```text +pragma circom 2.0.0; + +bus b(n){ + signal array[n]; +} + +template A(){ + signal input in; + b(in) out; + for(i = 0; i < in; i++){ + out.array[i] <== i; + } +} + +component main = A(); +``` + +In the code above, the array inside the bus `out` is initialized depending on the size of signal `in`, whose value is unknown at compilation time. Thus, the compiler arises an error: +```` +error[T20467]: Typing error found + ┌─ "pruebas.circom":9:4 + │ +9 │ b(in) out; + │ ^^^^^^^^^ Parameters of a bus must be known during the constraint generation phase + +previous errors were found``` + + + ## Control Flow If `if-else` or `for-loop`blocks have unknown conditions, then the block is considered unknown and no constraint can be generated inside it. Consequently, constraint can only be generated in a control flow with known conditions. diff --git a/mkdocs/docs/circom-language/formats/constraints-json.md b/mkdocs/docs/circom-language/formats/constraints-json.md index cff7e63dc..31b9be1a7 100644 --- a/mkdocs/docs/circom-language/formats/constraints-json.md +++ b/mkdocs/docs/circom-language/formats/constraints-json.md @@ -47,19 +47,21 @@ template Main() { if we run ```text -circom simplify.circom --json --wasm +circom basic.circom --json --wasm ``` -a file 'basic_contraints.json' is generated that contains +a file 'basic_contraints.json' is generated and it contains two constraints: ```text { "constraints": [ -[{"2":"21888242871839275222246405745257275088548364400416034343698204186575808495616"},{"0":"1","2":"2","3":"1"},{"1":"21888242871839275222246405745257275088548364400416034343698204186575808495616"}] +[{"2":"21888242871839275222246405745257275088548364400416034343698204186575808495616"},{"4":"1"},{"1":"21888242871839275222246405745257275088548364400416034343698204186575808495616"}], +[{},{},{"0":"1","2":"2","3":"1","4":"21888242871839275222246405745257275088548364400416034343698204186575808495616"}] ] } ``` -where we can see that only one constraint is taken after applying the simplification (since the --O2 simplification is the default). + As we can see, only constant and renaming (equalities between signals) simplifications have been aplied +(since the --O1 simplification is the default). Instead, if we run @@ -82,16 +84,15 @@ to indicate that we do not want to apply any simplification the generated file ' Finaly, if we run ```text -circom basic.circom --json --wasm --O1 +circom basic.circom --json --wasm --O2 ``` -to indicate that we only want to apply constant and renaming (equalities between signals) simplifications, the generated file 'basic_constraints.json' contains +we can see that only one constraint is taken after applying the full simplification: ```text { "constraints": [ -[{"2":"21888242871839275222246405745257275088548364400416034343698204186575808495616"},{"4":"1"},{"1":"21888242871839275222246405745257275088548364400416034343698204186575808495616"}], -[{},{},{"0":"1","2":"2","3":"1","4":"21888242871839275222246405745257275088548364400416034343698204186575808495616"}] +[{"2":"21888242871839275222246405745257275088548364400416034343698204186575808495616"},{"0":"1","2":"2","3":"1"},{"1":"21888242871839275222246405745257275088548364400416034343698204186575808495616"}] ] } ``` diff --git a/mkdocs/docs/circom-language/formats/simplification-json.md b/mkdocs/docs/circom-language/formats/simplification-json.md index 7e582f6fc..4d1034e83 100644 --- a/mkdocs/docs/circom-language/formats/simplification-json.md +++ b/mkdocs/docs/circom-language/formats/simplification-json.md @@ -47,12 +47,11 @@ a file 'simplify_substitutions.json' is generated that contains ```text { "5" : {"2":"1"}, -"4" : {"1":"1"}, -"6" : {"0":"1","2":"2","3":"1"} +"4" : {"1":"1"} } ``` -where we can see that three signals have been substituted (since the --O2 simplification is the default). +where we can see that two signals have been substituted (since the `--O1` simplification is the default). Instead, if we run @@ -69,14 +68,15 @@ to indicate that we do not want to apply any simplification, the generated file Finally, if we run ```text -circom simplify.circom --r1cs --wasm --simplification_substitution --O1 +circom simplify.circom --r1cs --wasm --simplification_substitution --O2 ``` -to indicate that we only want to apply constant and renaming (equalities between signals) simplifications, the generated file 'simplify_substitutions.json' contains +to indicate that we want to apply the full form of simplification, the generated file 'simplify_substitutions.json' contains: ```text { "5" : {"2":"1"}, -"4" : {"1":"1"} +"4" : {"1":"1"}, +"6" : {"0":"1","2":"2","3":"1"} } ``` diff --git a/mkdocs/docs/circom-language/formats/sym.md b/mkdocs/docs/circom-language/formats/sym.md index a64cfc0a7..ea7dd7cf9 100644 --- a/mkdocs/docs/circom-language/formats/sym.md +++ b/mkdocs/docs/circom-language/formats/sym.md @@ -48,10 +48,10 @@ a file 'symbols.sym' is generated that contains 3,3,1,main.in[1] 4,-1,0,main.c.out 5,-1,0,main.c.in[0] -6,-1,0,main.c.in[1] +6,4,0,main.c.in[1] ``` -where we can see that three signals have been eliminated (since the --O2 simplification is the default). +where we can see that two signals have been eliminated (since the `--O1` simplification is the default). Instead, if we run @@ -72,10 +72,10 @@ to indicate that we do not want to apply any simplification the generated file ' Finally, if we run ```text -circom symbols.circom --r1cs --wasm --sym --O1 +circom symbols.circom --r1cs --wasm --sym --O2 ``` -to indicate that we only want to apply constant and renaming (equalities between signals) simplifications the generated file 'symbols.sym' contains +to indicate that we want to apply the full form of simplification, the generated file 'symbols.sym' contains ```text 1,1,1,main.out @@ -83,5 +83,5 @@ to indicate that we only want to apply constant and renaming (equalities between 3,3,1,main.in[1] 4,-1,0,main.c.out 5,-1,0,main.c.in[0] -6,4,0,main.c.in[1] +6,-1,0,main.c.in[1] ``` diff --git a/mkdocs/docs/circom-language/reserved-keywords.md b/mkdocs/docs/circom-language/reserved-keywords.md index a0cd7e4fd..07b6e5741 100644 --- a/mkdocs/docs/circom-language/reserved-keywords.md +++ b/mkdocs/docs/circom-language/reserved-keywords.md @@ -1,7 +1,7 @@ # Reserved Keywords The list of reserved keywords is the following: - + * **signal:** Declare a new signal. * **input:** Declare the signal as input. * **output:** Declare the signal as output. @@ -22,5 +22,6 @@ The list of reserved keywords is the following: * **parallel:** To generate C code with the parallel component or template. * **pragma circom**: Instruction to check the compiler version. * **pragma custom_templates**: Instruction to indicate the usage of custom templates. +* **bus**: Definition of a bus of signals. diff --git a/mkdocs/docs/circom-language/scoping.md b/mkdocs/docs/circom-language/scoping.md index f6d64bf85..233e46b77 100644 --- a/mkdocs/docs/circom-language/scoping.md +++ b/mkdocs/docs/circom-language/scoping.md @@ -1,6 +1,6 @@ # Scoping -Circom has static scoping like C and Rust. However, we have that signals and components must have global scoping and hence they should be defined at the top-level block of the template that defines them or, since circom 2.1.5, inside (nested) `if` blocks, but only if conditions are known at compilation time. +Circom has static scoping like C and Rust. However, we have that signals, buses and components must have global scoping and hence they should be defined at the top-level block of the template that defines them or, since circom 2.1.5, inside (nested) `if` blocks, but only if conditions are known at compilation time. ```text pragma circom 2.1.5; diff --git a/mkdocs/docs/circom-language/tags.md b/mkdocs/docs/circom-language/tags.md index 1ad4e56af..b6b957a52 100644 --- a/mkdocs/docs/circom-language/tags.md +++ b/mkdocs/docs/circom-language/tags.md @@ -112,3 +112,19 @@ template A(){ } ``` The compilation of the previous code throws the next error "Invalid assignment: tags cannot be assigned to a signal already initialized", since a position of the array (out[0]) already has a value, then the value of max cannot be modified after the first assignment. + +## Tags in buses +Similar to signals, buses and their fields can also be tagged in their declarations. + +``` +bus Book { + signal {maxvalue} title[50]; + signal pages; + signal {maxvalue} year; +}; + +bus Person{ + signal name[50]; + Book {old} name; +} +``` \ No newline at end of file diff --git a/mkdocs/docs/getting-started/compilation-options.md b/mkdocs/docs/getting-started/compilation-options.md index 229e4628e..aec3befd1 100644 --- a/mkdocs/docs/getting-started/compilation-options.md +++ b/mkdocs/docs/getting-started/compilation-options.md @@ -39,7 +39,7 @@ In the following, we explain these options. ##### Flags and options related to the compiler's output * Flag ```--r1cs``` outputs the constraints in binary R1CS format (see the detailed format [here](https://github.com/iden3/r1csfile/blob/master/doc/r1cs_bin_format.md)). * Flag ```--sym``` outputs for every signal of the circuit: the unique number given by the compiler, the circom qualified name, the number of the witness signal that contains it and the (unique) number of the component (given by the compiler) it belongs (see the detailed format and examples [here](../circom-language/formats/sym.md)). -* Flag ```--simplification_substitution``` outputs the substitutions performed by the --O1 and --O2 (default) constraint simplification options in json format (see the detailed format [here](../circom-language/formats/simplification-json.md)). +* Flag ```--simplification_substitution``` outputs the substitutions performed by the --O1 (default) and --O2 constraint simplification options in json format (see the detailed format [here](../circom-language/formats/simplification-json.md)). * Flag ```--wasm``` produces a WebAssembly program that receives the private and public inputs and generates the circuit witness. * Flag ```-c / --c``` produces a C++ program that receives the private and public inputs and generates the circuit witness. * Flag ```--wat``` compiles the circuit to wat. diff --git a/mkdocs/mkdocs.yml b/mkdocs/mkdocs.yml index 59ed4c0e6..c553bd39c 100644 --- a/mkdocs/mkdocs.yml +++ b/mkdocs/mkdocs.yml @@ -58,6 +58,8 @@ nav: - New features circom 2.1: - Anonymous Components and tuples: 'circom-language/anonymous-components-and-tuples.md' - Tags: 'circom-language/tags.md' + - New features circom 2.2: + - Buses: 'circom-language/buses.md' - Code Quality: - Code Assertion: 'circom-language/code-quality/code-assertion.md' - Debugging Operations: 'circom-language/code-quality/debugging-operations.md' diff --git a/npm/bin/circom.wasm b/npm/bin/circom.wasm index 8acb4ac7d..113844c18 100755 Binary files a/npm/bin/circom.wasm and b/npm/bin/circom.wasm differ diff --git a/npm/package-lock.json b/npm/package-lock.json index d0aa380a4..84f982e0b 100644 --- a/npm/package-lock.json +++ b/npm/package-lock.json @@ -1,12 +1,12 @@ { "name": "@distributedlab/circom2", - "version": "0.2.19-rc.1", + "version": "0.2.20-rc.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@distributedlab/circom2", - "version": "0.2.19-rc.1", + "version": "0.2.20-rc.0", "license": "GPL-3.0", "dependencies": { "@wasmer/wasi": "^0.12.0" diff --git a/npm/package.json b/npm/package.json index d194c8b6e..cfe59c64f 100644 --- a/npm/package.json +++ b/npm/package.json @@ -1,6 +1,6 @@ { "name": "@distributedlab/circom2", - "version": "0.2.19-rc.1", + "version": "0.2.20-rc.0", "description": "Circom 2.0 in WebAssembly", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/parser/Cargo.toml b/parser/Cargo.toml index 0af53cf73..b16fcdf7b 100644 --- a/parser/Cargo.toml +++ b/parser/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parser" -version = "2.1.9" +version = "2.2.0" authors = ["Costa Group UCM","iden3"] edition = "2018" build = "build.rs" @@ -13,7 +13,7 @@ num-traits = "0.2.6" [dependencies] program_structure = {path = "../program_structure"} -lalrpop-util = "0.19.9" +lalrpop-util = { version="0.19.9", features = ["lexer"]} regex = "1.1.2" rustc-hex = "2.0.1" num-bigint-dig = "0.6.0" diff --git a/parser/src/lang.lalrpop b/parser/src/lang.lalrpop index 90dc96f74..e71e69630 100644 --- a/parser/src/lang.lalrpop +++ b/parser/src/lang.lalrpop @@ -9,8 +9,7 @@ use program_structure::ast::produce_report; use program_structure::error_definition::Report; use program_structure::error_code::ReportCode; -grammar<'err>(file_id: usize, errors:&'err mut Vec); - +grammar<'err>(file_id: usize, errors:&'err mut Vec, field: &BigInt); CommaSepList:Vec = { ",")*> => { e.push(t); @@ -110,6 +109,14 @@ pub ParseMainComponent : MainComponent = { }, }; +pub ParseParenthesisArguments : Vec = { + "(" ")" =>{ + match arg_names { + None => Vec::new(), + Some(a) => a + } + }, +}; pub ParseDefinition : Definition = { "function" "(" ")" @@ -119,13 +126,24 @@ pub ParseDefinition : Definition = { Some(a) => build_function(Meta::new(s,e),name,a,args..arge,body), }, - "template" "(" ")" + "template" => match arg_names { None => build_template(Meta::new(s,e), name, Vec::new(), args..arge, body, parallel.is_some(), custom_gate.is_some()), Some(a) => build_template(Meta::new(s,e), name, a, args..arge, body, parallel.is_some(), custom_gate.is_some()), }, + + "bus" + => { + match arg_names{ + None => + build_bus(Meta::new(s,e), name, Vec::new(), args..arge, body), + Some(a) => + build_bus(Meta::new(s,e), name, a, args..arge, body), + } + }, + }; @@ -145,18 +163,72 @@ ParseSignalType: SignalType = { "output" => SignalType::Output }; + + +ParseTagsVector: Vec = { + + => { + match tags_list { + None => Vec::new(), + Some(tl) => tl, + } + } +} + SignalHeader : VariableType = { - "signal" + "signal" => { - let s = match signal_type { + let signal_type = match signal_type { None => SignalType::Intermediate, Some(st) => st, }; - let t = match tags_list { - None => Vec::new(), - Some(tl) => tl, + VariableType::Signal(signal_type, tags_list) + }, + "signal" + => { + VariableType::Signal(signal_type, tags_list) + } +}; + +BusHeader : (Expression, VariableType) = { + + => { + let wire = match wire_type { + None => SignalType::Intermediate, + Some(st) => st, + }; + let bus_builder = build_bus_call(Meta::new(s,e),id.clone(),Vec::new()); + (bus_builder, VariableType::Bus(id, wire, tags_list)) + }, + + "(" ")" + + => { + let wire = match wire_type { + None => SignalType::Intermediate, + Some(st) => st, }; - VariableType::Signal(s, t) + let bus_builder = match args { + None => build_bus_call(Meta::new(s,e),id.clone(),Vec::new()), + Some(a) => build_bus_call(Meta::new(s,e),id.clone(),a), + }; + (bus_builder,VariableType::Bus(id, wire, tags_list)) + }, + + + => { + let bus_builder = build_bus_call(Meta::new(s,e),id.clone(),Vec::new()); + (bus_builder, VariableType::Bus(id, wire, tags_list)) + }, + + "(" ")" + + => { + let bus_builder = match args { + None => build_bus_call(Meta::new(s,e),id.clone(),Vec::new()), + Some(a) => build_bus_call(Meta::new(s,e),id.clone(),a), + }; + (bus_builder,VariableType::Bus(id, wire, tags_list)) } }; @@ -243,6 +315,7 @@ ParseDeclaration : Statement = { symbols.push(symbol); ast_shortcuts::split_declaration_into_single_nodes_and_multisubstitution(meta,xtype,symbols, init) }, + "component" "(" ",")*> ")" => { let mut symbols = symbols; let meta = Meta::new(s,e); @@ -268,21 +341,40 @@ ParseDeclaration : Statement = { ast_shortcuts::split_declaration_into_single_nodes(meta,xtype,symbols,AssignOp::AssignVar) }, - ",")*> + ",")*> => { let mut symbols = symbols; let meta = Meta::new(s,e); symbols.push(symbol); ast_shortcuts::split_declaration_into_single_nodes(meta,xtype,symbols,AssignOp::AssignConstraintSignal) }, - ",")*> + ",")*> => { let mut symbols = symbols; let meta = Meta::new(s,e); symbols.push(symbol); ast_shortcuts::split_declaration_into_single_nodes(meta,xtype,symbols,AssignOp::AssignSignal) }, + + ",")*> => { + let (bus_type,xtype) = bus_header; + let mut symbols = symbols; + let meta = Meta::new(s,e); + symbols.push(symbol); + ast_shortcuts::split_bus_declaration_into_single_nodes(meta,bus_type,xtype,symbols,AssignOp::AssignConstraintSignal) + }, + + ",")*> => { + let (bus_type,xtype) = bus_header; + let mut symbols = symbols; + let meta = Meta::new(s,e); + symbols.push(symbol); + ast_shortcuts::split_bus_declaration_into_single_nodes(meta,bus_type,xtype,symbols,AssignOp::AssignSignal) +}, + + }; + ParseSubstitution : Statement = { => {if let Expression::Variable {meta, name, access} = variable { @@ -342,10 +434,10 @@ ParseSubstitution : Statement = { => ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::BitXor,Meta::new(s,e),variable,rhe), "++" - => ast_shortcuts::plusplus(Meta::new(s,e),variable), + => ast_shortcuts::plusplus(Meta::new(s,e),variable, field), "--" - => ast_shortcuts::subsub(Meta::new(s,e),variable), + => ast_shortcuts::subsub(Meta::new(s,e),variable, field), "++" => { @@ -399,6 +491,7 @@ ParseStatement1 : Statement = { => build_conditional_block(Meta::new(s,e),cond,if_case,Option::Some(else_case)), ParseStatement2 }; + ParseStatement2 : Statement = { "for" "(" Semicolon Semicolon ")" => ast_shortcuts::for_into_while(Meta::new(s,e),init,cond,step,body), @@ -423,8 +516,17 @@ ParseStatement2 : Statement = { "assert" "(" ")" Semicolon => build_assert(Meta::new(s,e),arg), - Semicolon - => build_anonymous_component_statement(Meta::new(s,e), lhe), + Semicolon + => { + match lhe { + Expression::AnonymousComp { .. } => build_anonymous_component_statement(Meta::new(s,e), lhe), + _ => { + errors.push(produce_report(ReportCode::IllegalExpression, s..e, file_id)); + //doesn't matter + build_log_call(Meta::new(s,e),Vec::new()) + } , + } + }, ParseBlock }; @@ -456,7 +558,7 @@ ParseVarAccess : Access = { => build_component_access(component_acc), }; ParseArrayAcc: Expression = { - "[""]" => dim + "[" "]" => dim }; ParseComponentAcc: String = { "." => id, @@ -607,7 +709,8 @@ Expression3 = InfixOpTier; // ops: Unary - ! ~ Expression2 = PrefixOpTier; -ExpressionAnonymous: Expression = { +// function call, array inline, anonymous component call +Expression1: Expression = { "(" ")" "(" ")" => {let params = match args { None => Vec::new(), @@ -618,11 +721,7 @@ ExpressionAnonymous: Expression = { Some(a) => a }; build_anonymous_component(Meta::new(s,e),id,params,signals,names,false)} -} - -// function call, array inline, anonymous component call -Expression1: Expression = { - ExpressionAnonymous, + , "(" ")" => match args { @@ -653,21 +752,13 @@ Expression0: Expression = { => build_variable(Meta::new(s,e),"_".to_string(),Vec::new()), - => build_number(Meta::new(s,e),value), + => build_number(Meta::new(s,e),value, field), - => build_number(Meta::new(s,e),value), + => build_number(Meta::new(s,e),value, field), "(" ")", - - => match <>.error { - ParseError::UnrecognizedToken { ref token, .. } => { - errors.push(produce_report(ReportCode::IllegalExpression, token.0..token.2, file_id)); - // doesn't matter - build_number(Meta::new(0,0),BigInt::from(0)) - } - _ => unreachable!(), - } + }; // ==================================================================== @@ -765,4 +856,4 @@ Version : Version = { "." "." => { (version, subversion, subsubversion) } -}; +}; \ No newline at end of file diff --git a/parser/src/lib.rs b/parser/src/lib.rs index f2f9355a9..d3f12134d 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -12,6 +12,7 @@ mod parser_logic; mod syntax_sugar_remover; use include_logic::{FileStack, IncludesGraph}; +use num_bigint::BigInt; use program_structure::ast::{produce_compiler_version_report, produce_report, produce_report_with_message, produce_version_warning_report, Expression}; use program_structure::error_code::ReportCode; use program_structure::error_definition::ReportCollection; @@ -62,6 +63,7 @@ pub fn run_parser( file: String, version: &str, link_libraries: Vec, + field: &BigInt, save_ast: bool, ast_path: PathBuf, ) -> Result<(ProgramArchive, ReportCollection), (FileLibrary, ReportCollection)> { @@ -83,7 +85,7 @@ pub fn run_parser( } let file_id = file_library.add_file(path.clone(), src.clone()); let program = - parser_logic::parse_file(&src, file_id).map_err(|e| (file_library.clone(), e))?; + parser_logic::parse_file(&src, file_id, field).map_err(|e| (file_library.clone(), e))?; if save_ast { ast_list.push(program.clone()); } @@ -288,4 +290,4 @@ fn check_custom_gates_version( } } Ok(()) -} \ No newline at end of file +} diff --git a/parser/src/parser_logic.rs b/parser/src/parser_logic.rs index 0ea4bc48f..04a28b772 100644 --- a/parser/src/parser_logic.rs +++ b/parser/src/parser_logic.rs @@ -1,5 +1,6 @@ use super::lang; -use program_structure::ast::AST; +use num_bigint::BigInt; +use program_structure::ast::{AST}; use program_structure::ast::produce_report; use program_structure::error_code::ReportCode; use program_structure::error_definition::{ReportCollection, Report}; @@ -83,14 +84,14 @@ pub fn preprocess(expr: &str, file_id: FileID) -> Result Result { +pub fn parse_file(src: &str, file_id: FileID, field: &BigInt) -> Result { use lalrpop_util::ParseError::*; let mut errors = Vec::new(); let preprocess = preprocess(src, file_id)?; let ast = lang::ParseAstParser::new() - .parse(file_id, &mut errors, &preprocess) + .parse(file_id, &mut errors, field, &preprocess) // TODO: is this always fatal? .map_err(|parse_error| match parse_error { InvalidToken { location } => diff --git a/parser/src/syntax_sugar_remover.rs b/parser/src/syntax_sugar_remover.rs index 0c47d7af1..ffc773468 100644 --- a/parser/src/syntax_sugar_remover.rs +++ b/parser/src/syntax_sugar_remover.rs @@ -236,6 +236,14 @@ pub fn check_anonymous_components_expression( } Result::Ok(()) }, + BusCall { meta, args, .. } => { + for value in args{ + if value.contains_anonymous_comp() { + return Result::Err(anonymous_general_error(meta.clone(),"An anonymous component cannot be used as a parameter in a bus call ".to_string())); + } + } + Result::Ok(()) + }, AnonymousComp {meta, params, signals, .. } => { for value in params{ if value.contains_anonymous_comp() { @@ -497,19 +505,13 @@ pub fn remove_anonymous_from_expression( else{ let inputs = template.unwrap().get_declaration_inputs(); let mut n_expr = 0; - if inputs.len() != signals.len() { return Result::Err(anonymous_general_error(meta.clone(),"The number of template input signals must coincide with the number of input parameters ".to_string())); } - for value in signals { inputs_to_assignments.insert(inputs[n_expr].0.clone(), (AssignOp::AssignConstraintSignal, value)); n_expr += 1; - } - - if inputs.len() != inputs_to_assignments.len() { - return Result::Err(anonymous_general_error(meta.clone(),"The number of template input signals must coincide with the number of input parameters ".to_string())); - } + } } @@ -764,6 +766,14 @@ pub fn check_tuples_expression(exp: &Expression) -> Result<(), Report>{ } Result::Ok(()) }, + BusCall { meta, args, .. } => { + for value in args{ + if value.contains_tuple() { + return Result::Err(tuple_general_error(meta.clone(),"A tuple cannot be used as a parameter of a bus call".to_string())); + } + } + Result::Ok(()) + }, AnonymousComp { .. } => { unreachable!(); } @@ -927,5 +937,4 @@ pub fn remove_tuple_from_expression(exp : Expression) -> Expression{ }, _ => exp, } -} - +} \ No newline at end of file diff --git a/program_structure/Cargo.toml b/program_structure/Cargo.toml index 19417f0aa..6b88c1df9 100644 --- a/program_structure/Cargo.toml +++ b/program_structure/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "program_structure" -version = "2.1.9" +version = "2.2.0" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/program_structure/src/abstract_syntax_tree/ast.rs b/program_structure/src/abstract_syntax_tree/ast.rs index 73bb8b755..5274b91a0 100644 --- a/program_structure/src/abstract_syntax_tree/ast.rs +++ b/program_structure/src/abstract_syntax_tree/ast.rs @@ -155,6 +155,13 @@ pub enum Definition { arg_location: FileLocation, body: Statement, }, + Bus { + meta: Meta, + name: String, + args: Vec, + arg_location: FileLocation, + body: Statement, + }, } pub fn build_template( meta: Meta, @@ -178,6 +185,16 @@ pub fn build_function( Definition::Function { meta, name, args, arg_location, body } } +pub fn build_bus( + meta: Meta, + name: String, + args: Vec, + arg_location: FileLocation, + body: Statement, +) -> Definition { + Definition::Bus { meta, name, args, arg_location, body } +} + #[derive(Clone, Serialize)] pub enum Statement { IfThenElse { @@ -253,12 +270,14 @@ pub enum SignalType { pub type TagList = Vec; -#[derive(Clone, PartialEq, Ord, PartialOrd, Eq, Serialize)] + +#[derive(Clone, PartialEq, Eq, Serialize)] pub enum VariableType { Var, Signal(SignalType, TagList), Component, AnonymousComponent, + Bus(String, SignalType, TagList), } #[derive(Clone, Serialize)] @@ -295,6 +314,11 @@ pub enum Expression { id: String, args: Vec, }, + BusCall { + meta: Meta, + id: String, + args: Vec, + }, AnonymousComp{ meta: Meta, id: String, @@ -370,10 +394,11 @@ pub enum ExpressionPrefixOpcode { // Knowledge buckets -#[derive(Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Serialize)] +#[derive(Clone, PartialOrd, PartialEq, Ord, Eq, Serialize)] pub enum TypeReduction { Variable, - Component, + Component(Option), + Bus(Option), Signal, Tag, } @@ -404,7 +429,7 @@ impl TypeKnowledge { } pub fn get_reduces_to(&self) -> TypeReduction { if let Option::Some(t) = &self.reduces_to { - *t + t.clone() } else { panic!("reduces_to knowledge is been look at without being initialized"); } @@ -412,8 +437,18 @@ impl TypeKnowledge { pub fn is_var(&self) -> bool { self.get_reduces_to() == TypeReduction::Variable } + + pub fn is_initialized(&self) -> bool { + if let Option::Some(_) = &self.reduces_to { + true + } else { + false + } + } pub fn is_component(&self) -> bool { - self.get_reduces_to() == TypeReduction::Component + if let TypeReduction::Component(_) = self.get_reduces_to() { + true + } else { false } } pub fn is_signal(&self) -> bool { self.get_reduces_to() == TypeReduction::Signal @@ -421,6 +456,13 @@ impl TypeKnowledge { pub fn is_tag(&self) -> bool { self.get_reduces_to() == TypeReduction::Tag } + pub fn is_bus(&self) -> bool { + if let TypeReduction::Bus(_) = self.get_reduces_to() { + true + } else { + false + } + } } #[derive(Default, Clone, Serialize)] @@ -613,4 +655,4 @@ pub fn tuple_general_error(meta : Meta, msg : String) -> Report { "This is the tuple whose use is not allowed".to_string(), ); report -} \ No newline at end of file +} diff --git a/program_structure/src/abstract_syntax_tree/ast_shortcuts.rs b/program_structure/src/abstract_syntax_tree/ast_shortcuts.rs index 08fb66c0a..b0a301364 100644 --- a/program_structure/src/abstract_syntax_tree/ast_shortcuts.rs +++ b/program_structure/src/abstract_syntax_tree/ast_shortcuts.rs @@ -27,13 +27,13 @@ pub fn assign_with_op_shortcut( build_substitution(meta, var, access, AssignOp::AssignVar, infix) } -pub fn plusplus(meta: Meta, variable: (String, Vec)) -> Statement { - let one = build_number(meta.clone(), BigInt::from(1)); +pub fn plusplus(meta: Meta, variable: (String, Vec), field: &BigInt) -> Statement { + let one = build_number(meta.clone(), BigInt::from(1), field); assign_with_op_shortcut(ExpressionInfixOpcode::Add, meta, variable, one) } -pub fn subsub(meta: Meta, variable: (String, Vec)) -> Statement { - let one = build_number(meta.clone(), BigInt::from(1)); +pub fn subsub(meta: Meta, variable: (String, Vec), field: &BigInt) -> Statement { + let one = build_number(meta.clone(), BigInt::from(1), field); assign_with_op_shortcut(ExpressionInfixOpcode::Sub, meta, variable, one) } @@ -136,3 +136,73 @@ pub fn split_declaration_into_single_nodes_and_multisubstitution( } build_initialization_block(meta, xtype, initializations) } + + + +pub fn split_bus_declaration_into_single_nodes( + meta: Meta, + bustype: Expression, + xtype: VariableType, + symbols: Vec, + op: AssignOp, +) -> Statement { + use crate::ast_shortcuts::AssignOp::AssignVar; + + let mut initializations = Vec::new(); + + for symbol in symbols { + let with_meta = meta.clone(); + let has_type = xtype.clone(); + let name = symbol.name.clone(); + let dimensions = symbol.is_array; + let possible_init = symbol.init; + let single_declaration = build_declaration(with_meta, has_type, name, dimensions.clone()); + + let mut value = bustype.clone(); + for dim_expr in dimensions.iter().rev(){ + value = build_uniform_array(meta.clone(), value, dim_expr.clone()); + } + + let bus_declaration = build_substitution(meta.clone(), symbol.name.clone(), vec![], AssignVar, value); + initializations.push(single_declaration); + initializations.push(bus_declaration); + + if let Option::Some(init) = possible_init { + let substitution = + build_substitution(meta.clone(), symbol.name, vec![], op, init); + initializations.push(substitution); + } + } + build_initialization_block(meta, xtype, initializations) +} + +pub fn split_bus_declaration_into_single_nodes_and_multisubstitution( + meta: Meta, + bustype: Expression, + xtype: VariableType, + symbols: Vec, + init: Option, +) -> Statement { + use crate::ast_shortcuts::AssignOp::AssignVar; + + let mut initializations = Vec::new(); + let mut values = Vec::new(); + for symbol in symbols { + let with_meta = meta.clone(); + let has_type = xtype.clone(); + let name = symbol.name.clone(); + let dimensions = symbol.is_array; + debug_assert!(symbol.init.is_none()); + let single_declaration = build_declaration(with_meta.clone(), has_type, name.clone(), dimensions.clone()); + let bus_declaration = build_substitution(meta.clone(), symbol.name.clone(), vec![], AssignVar, bustype.clone()); + initializations.push(single_declaration); + initializations.push(bus_declaration); + values.push(Expression::Variable { meta: with_meta.clone(), name: name, access: Vec::new() }) + } + if let Some(tuple) = init { + let (op,expression) = tuple.tuple_init; + let multi_sub = build_mult_substitution(meta.clone(), build_tuple(meta.clone(), values), op, expression); + initializations.push(multi_sub); + } + build_initialization_block(meta, xtype, initializations) +} diff --git a/program_structure/src/abstract_syntax_tree/expression_builders.rs b/program_structure/src/abstract_syntax_tree/expression_builders.rs index 0e7d2a0ba..287b156af 100644 --- a/program_structure/src/abstract_syntax_tree/expression_builders.rs +++ b/program_structure/src/abstract_syntax_tree/expression_builders.rs @@ -43,7 +43,11 @@ pub fn build_variable(meta: Meta, name: String, access: Vec) -> Expressi Variable { meta, name, access } } -pub fn build_number(meta: Meta, value: BigInt) -> Expression { +pub fn build_number(meta: Meta, value: BigInt, field: &BigInt) -> Expression { + Expression::Number(meta, value % field) +} + +pub fn build_number_without_field(meta: Meta, value: BigInt) -> Expression { Expression::Number(meta, value) } @@ -51,6 +55,10 @@ pub fn build_call(meta: Meta, id: String, args: Vec) -> Expression { Call { meta, id, args } } +pub fn build_bus_call(meta: Meta, id: String, args: Vec) -> Expression { + BusCall { meta, id, args } +} + pub fn build_anonymous_component(meta: Meta, id: String, params: Vec, signals: Vec, names: Option>, is_parallel: bool) -> Expression { AnonymousComp { meta, id, params, signals, names, is_parallel } } diff --git a/program_structure/src/abstract_syntax_tree/expression_impl.rs b/program_structure/src/abstract_syntax_tree/expression_impl.rs index 900827e6f..c122447e5 100644 --- a/program_structure/src/abstract_syntax_tree/expression_impl.rs +++ b/program_structure/src/abstract_syntax_tree/expression_impl.rs @@ -14,11 +14,13 @@ impl Expression { | Number(meta, ..) | Call { meta, .. } | AnonymousComp { meta, ..} - | ArrayInLine { meta, .. } => meta, - | UniformArray { meta, .. } => meta, - | Tuple {meta, ..} => meta, + | ArrayInLine { meta, .. } + | UniformArray { meta, .. } + | Tuple {meta, ..} + | BusCall { meta, .. } => meta, } } + pub fn get_mut_meta(&mut self) -> &mut Meta { use Expression::*; match self { @@ -30,9 +32,10 @@ impl Expression { | Number(meta, ..) | Call { meta, .. } | AnonymousComp {meta, ..} - | ArrayInLine { meta, .. } => meta, - | UniformArray { meta, .. } => meta, - | Tuple {meta, ..} => meta, + | ArrayInLine { meta, .. } + | UniformArray { meta, .. } + | Tuple {meta, ..} + | BusCall {meta, ..} => meta, } } @@ -73,6 +76,7 @@ impl Expression { false } } + pub fn is_switch(&self) -> bool { use Expression::*; if let InlineSwitchOp { .. } = self { @@ -118,6 +122,26 @@ impl Expression { } } + pub fn is_bus_call(&self) -> bool { + use Expression::*; + if let BusCall { .. } = self { + true + } else { + false + } + } + + pub fn is_bus_call_array(&self) -> bool { + use Expression::*; + if let BusCall { .. } = self { + true + } else if let UniformArray { value, .. } = self { + value.is_bus_call_array() + } else { + false + } + } + pub fn is_anonymous_comp(&self) -> bool { use Expression::*; if let AnonymousComp { .. } = self { @@ -149,7 +173,7 @@ impl Expression { InlineSwitchOp { cond, if_true, if_false, .. } => { cond.contains_anonymous_comp() || if_true.contains_anonymous_comp() || if_false.contains_anonymous_comp() }, - Call { args, .. } | Tuple {values: args, ..} | ArrayInLine { values : args, .. } => { + BusCall { args, .. } | Call { args, .. } | Tuple {values: args, ..} | ArrayInLine { values : args, .. } => { for arg in args{ if arg.contains_anonymous_comp() { return true;} } @@ -182,7 +206,7 @@ impl Expression { InlineSwitchOp { cond, if_true, if_false, .. } => { cond.contains_tuple() || if_true.contains_tuple() || if_false.contains_tuple() }, - Call { args, .. } | ArrayInLine { values : args, .. } => { + BusCall{ args, .. } | Call { args, .. } | ArrayInLine { values : args, .. } => { for arg in args{ if arg.contains_tuple() { return true;} } @@ -228,6 +252,7 @@ impl FillMeta for Expression { fill_inline_switch_op(meta, cond, if_true, if_false, file_id, element_id) } Call { meta, args, .. } => fill_call(meta, args, file_id, element_id), + BusCall { meta, args, .. } => fill_call(meta, args, file_id, element_id), ArrayInLine { meta, values, .. } => { fill_array_inline(meta, values, file_id, element_id) } @@ -341,4 +366,4 @@ fn fill_uniform_array( meta.set_file_id(file_id); value.fill(file_id, element_id); dimensions.fill(file_id, element_id); -} +} \ No newline at end of file diff --git a/program_structure/src/abstract_syntax_tree/statement_impl.rs b/program_structure/src/abstract_syntax_tree/statement_impl.rs index 7b8463e72..9fd59ccf7 100644 --- a/program_structure/src/abstract_syntax_tree/statement_impl.rs +++ b/program_structure/src/abstract_syntax_tree/statement_impl.rs @@ -13,8 +13,8 @@ impl Statement { | Block { meta, .. } | Assert { meta, .. } | ConstraintEquality { meta, .. } - | InitializationBlock { meta, .. } => meta, - | MultSubstitution { meta, ..} => meta, + | InitializationBlock { meta, .. } + | MultSubstitution { meta, ..} | UnderscoreSubstitution { meta, .. } => meta, } } @@ -30,8 +30,8 @@ impl Statement { | Block { meta, .. } | Assert { meta, .. } | ConstraintEquality { meta, .. } - | InitializationBlock { meta, .. } => meta, - | MultSubstitution { meta, ..} => meta, + | InitializationBlock { meta, .. } + | MultSubstitution { meta, ..} | UnderscoreSubstitution { meta, .. } => meta, } } diff --git a/program_structure/src/lib.rs b/program_structure/src/lib.rs index 3b078ccbc..134a2f55b 100644 --- a/program_structure/src/lib.rs +++ b/program_structure/src/lib.rs @@ -9,3 +9,4 @@ pub mod utils; pub use abstract_syntax_tree::*; pub use program_library::*; pub use utils::*; +pub use program_library::bus_data; \ No newline at end of file diff --git a/program_structure/src/program_library/bus_data.rs b/program_structure/src/program_library/bus_data.rs new file mode 100644 index 000000000..712497854 --- /dev/null +++ b/program_structure/src/program_library/bus_data.rs @@ -0,0 +1,164 @@ +use std::collections::HashMap; +use super::ast::{FillMeta, Statement, VariableType, SignalType}; +use super::file_definition::{FileID, FileLocation}; +use super::wire_data::*; + +pub type BusInfo = HashMap; + +#[derive(Clone)] +pub struct BusData { + file_id: FileID, + name: String, + body: Statement, + num_of_params: usize, + name_of_params: Vec, + param_location: FileLocation, + fields: WireInfo, + /* Only used to know the order in which fields are declared.*/ + field_declarations: WireDeclarationOrder, +} + +impl BusData { + pub fn new( + file_id: FileID, + name: String, + mut body: Statement, + num_of_params: usize, + name_of_params: Vec, + param_location: FileLocation, + elem_id: &mut usize, + ) -> BusData { + body.fill(file_id, elem_id); + let mut fields = WireInfo::new(); + let mut field_declarations = WireDeclarationOrder::new(); + fill_fields(&body, &mut fields, &mut field_declarations); + BusData { + file_id, + name, + body, + num_of_params, + name_of_params, + param_location, + fields, + field_declarations + } + } + pub fn copy( + file_id: FileID, + name: String, + body: Statement, + num_of_params: usize, + name_of_params: Vec, + param_location: FileLocation, + fields: WireInfo, + field_declarations: WireDeclarationOrder + ) -> BusData { + BusData { + file_id, + name, + body, + num_of_params, + name_of_params, + param_location, + fields, + field_declarations + } + } + pub fn get_file_id(&self) -> FileID { + self.file_id + } + pub fn get_body(&self) -> &Statement { + &self.body + } + pub fn get_body_as_vec(&self) -> &Vec { + match &self.body { + Statement::Block { stmts, .. } => stmts, + _ => panic!("Function body should be a block"), + } + } + pub fn get_mut_body(&mut self) -> &mut Statement { + &mut self.body + } + pub fn set_body(&mut self, body: Statement){ + self.body = body; + } + pub fn replace_body(&mut self, new: Statement) -> Statement { + std::mem::replace(&mut self.body, new) + } + pub fn get_mut_body_as_vec(&mut self) -> &mut Vec { + match &mut self.body { + Statement::Block { stmts, .. } => stmts, + _ => panic!("Function body should be a block"), + } + } + pub fn get_param_location(&self) -> FileLocation { + self.param_location.clone() + } + pub fn get_num_of_params(&self) -> usize { + self.num_of_params + } + pub fn get_name_of_params(&self) -> &Vec { + &self.name_of_params + } + pub fn get_name(&self) -> &str { + &self.name + } + pub fn get_field_info(&self, name: &str) -> Option<&WireData> { + self.fields.get(name) + } + pub fn get_fields(&self) -> &WireInfo { + &self.fields + } + pub fn get_declaration_fields(&self) -> &WireDeclarationOrder { + &self.field_declarations + } +} + + +fn fill_fields( + bus_statement: &Statement, + fields: &mut WireInfo, + field_declarations: &mut WireDeclarationOrder +) { + use Statement::*; + match bus_statement { + Block { stmts, .. } | InitializationBlock { initializations : stmts, .. } => { + for stmt in stmts.iter() { + fill_fields(stmt, fields, field_declarations); + } + } + Declaration { xtype, name, dimensions, .. } => { + match xtype { + VariableType::Signal(stype, tag_list) => { + if *stype == SignalType::Intermediate { + let wire_name = name.clone(); + let dim = dimensions.len(); + let mut tag_info = TagInfo::new(); + for tag in tag_list { + tag_info.insert(tag.clone()); + } + let field_data = WireData::new(WireType::Signal,dim,tag_info); + fields.insert(wire_name.clone(), field_data); + field_declarations.push((wire_name,dim)); + } + }, + VariableType::Bus(tname, stype, tag_list) => { + if *stype == SignalType::Intermediate { + let wire_name = name.clone(); + let dim = dimensions.len(); + let type_name = tname.clone(); + let mut tag_info = TagInfo::new(); + for tag in tag_list { + tag_info.insert(tag.clone()); + } + let field_data = WireData::new(WireType::Bus(type_name),dim,tag_info); + fields.insert(wire_name.clone(), field_data); + field_declarations.push((wire_name,dim)); + } + }, + _ => {}, + } + } + _ => {} + } +} \ No newline at end of file diff --git a/program_structure/src/program_library/error_code.rs b/program_structure/src/program_library/error_code.rs index 61ff8b1af..6318e712c 100644 --- a/program_structure/src/program_library/error_code.rs +++ b/program_structure/src/program_library/error_code.rs @@ -21,12 +21,15 @@ pub enum ReportCode { WrongTypesInAssignOperationOperatorSignal, WrongTypesInAssignOperationOperatorNoSignal, WrongTypesInAssignOperationTemplate, + WrongTypesInAssignOperationBus, WrongTypesInAssignOperationExpression, WrongTypesInAssignOperationArrayTemplates, + WrongTypesInAssignOperationArrayBuses, WrongTypesInAssignOperationDims(usize, usize), WrongNumberOfArguments(usize, usize), UndefinedFunction, UndefinedTemplate, + UndefinedBus, UninitializedSymbolInExpression, UnableToTypeFunction, UnreachableConstraints, @@ -34,6 +37,7 @@ pub enum ReportCode { UnreachableSignals, UnknownIndex, UnknownDimension, + UnknownTemplateAssignment, SameFunctionDeclaredTwice, SameTemplateDeclaredTwice, SameSymbolDeclaredTwice, @@ -61,6 +65,8 @@ pub enum ReportCode { ParallelOperatorWithWrongTypes, InfixOperatorWithWrongTypes, InvalidArgumentInCall, + InvalidArgumentInBusInstantiationT, + InvalidArgumentInBusInstantiationB, InconsistentReturnTypesInBlock, InconsistentStaticInformation, InvalidArrayAccess(usize, usize), @@ -69,7 +75,9 @@ pub enum ReportCode { InvalidTagAccessAfterArray, InvalidArraySize(usize), InvalidArraySizeT, + InvalidArraySizeB, InvalidArrayType, + InvalidArrayTypeB, ForStatementIllConstructed, BadArrayAccess, AssigningAComponentTwice, @@ -80,6 +88,7 @@ pub enum ReportCode { InvalidPartialArray, MustBeSingleArithmetic(usize), MustBeSingleArithmeticT, + MustBeSingleArithmeticB, MustBeArithmetic, OutputTagCannotBeModifiedOutside, MustBeSameDimension(usize, usize), @@ -87,6 +96,7 @@ pub enum ReportCode { RuntimeError, RuntimeWarning, UnknownTemplate, + UnknownBus, NonQuadratic, NonValidTagAssignment, NonConstantArrayLength, @@ -108,6 +118,10 @@ pub enum ReportCode { TupleError, InvalidSignalTagAccess, UninitializedComponent, + BusWrongNumberOfArguments, + InvalidSignalAccessInBus, + MustBeSameBus, + MustBeBus, } impl fmt::Display for ReportCode { @@ -118,12 +132,6 @@ impl fmt::Display for ReportCode { MultipleMain => "P1002", CompilerVersionError => "P1003", NoCompilerVersionWarning => "P1004", - WrongTypesInAssignOperationOperatorSignal => "T2000", - WrongTypesInAssignOperationOperatorNoSignal => "T2000", - WrongTypesInAssignOperationArrayTemplates => "T2000", - WrongTypesInAssignOperationTemplate => "T2000", - WrongTypesInAssignOperationExpression => "T2000", - WrongTypesInAssignOperationDims(..) => "T2000", UnclosedComment => "P1005", FileOs => "P1006", MissingSemicolon => "P1008", @@ -174,6 +182,7 @@ impl fmt::Display for ReportCode { InvalidArraySize(..) => "T2033", InvalidArraySizeT => "T2033", InvalidArrayType => "T2034", + InvalidArrayTypeB => "T2034", ForStatementIllConstructed => "T2035", BadArrayAccess => "T2035", AssigningAComponentTwice => "T2036", @@ -185,6 +194,7 @@ impl fmt::Display for ReportCode { InvalidPartialArray => "T2043", MustBeSingleArithmetic(..) => "T2044", MustBeSingleArithmeticT => "T2044", + MustBeSingleArithmeticB => "T2044", ExpectedDimDiffGotDim(..) => "T2045", MustBeSameDimension(..) => "T2046", MustBeArithmetic => "T2047", @@ -192,17 +202,29 @@ impl fmt::Display for ReportCode { UnreachableTags => "T2049", UnreachableSignals => "T2050", MainComponentWithTags => "T2051", - NonValidTagAssignment => "T2052", - IllegalMainExpression => "T2053", + UndefinedBus => "T2052", + InvalidArraySizeB => "T2053", + WrongTypesInAssignOperationOperatorSignal => "T2054", + WrongTypesInAssignOperationOperatorNoSignal => "T2055", + WrongTypesInAssignOperationArrayTemplates => "T2056", + WrongTypesInAssignOperationTemplate => "T2057", + WrongTypesInAssignOperationArrayBuses => "T2058", + WrongTypesInAssignOperationBus => "T2059", + WrongTypesInAssignOperationExpression => "T2060", + WrongTypesInAssignOperationDims(..) => "T2061", + NonValidTagAssignment => "T2062", + IllegalMainExpression => "T2063", RuntimeError => "T3001", RuntimeWarning => "T3002", UnknownDimension => "T20460", UnknownTemplate => "T20461", + UnknownTemplateAssignment => "T2O461-A", NonQuadratic => "T20462", NonConstantArrayLength => "T20463", NonComputableExpression => "T20464", WrongNumberOfArguments(..) => "T20465", UninitializedComponent => "T20466", + UnknownBus => "T20467", // Constraint analysis codes UnconstrainedSignal => "CA01", UnconstrainedIOSignal => "CA02", @@ -217,7 +239,13 @@ impl fmt::Display for ReportCode { AnonymousCompError => "TAC01", TupleError => "TAC02", UnderscoreWithNoSignalWarning => "TAC03", + BusWrongNumberOfArguments => "BU01", + InvalidArgumentInBusInstantiationT => "BU02", + InvalidArgumentInBusInstantiationB => "BU03", + InvalidSignalAccessInBus => "BU04", + MustBeSameBus => "BU05", + MustBeBus => "BU06", }; f.write_str(string_format) } -} +} \ No newline at end of file diff --git a/program_structure/src/program_library/mod.rs b/program_structure/src/program_library/mod.rs index 7b0e436bf..d19497041 100644 --- a/program_structure/src/program_library/mod.rs +++ b/program_structure/src/program_library/mod.rs @@ -1,4 +1,5 @@ use super::ast; +pub mod bus_data; pub mod error_code; pub mod error_definition; pub mod file_definition; @@ -6,3 +7,4 @@ pub mod function_data; pub mod program_archive; pub mod program_merger; pub mod template_data; +pub mod wire_data; \ No newline at end of file diff --git a/program_structure/src/program_library/program_archive.rs b/program_structure/src/program_library/program_archive.rs index 69f6d18a4..3a55d95bf 100644 --- a/program_structure/src/program_library/program_archive.rs +++ b/program_structure/src/program_library/program_archive.rs @@ -3,6 +3,7 @@ use super::file_definition::{FileID, FileLibrary}; use super::function_data::{FunctionData, FunctionInfo}; use super::program_merger::Merger; use super::template_data::{TemplateData, TemplateInfo}; +use super::bus_data::{BusData, BusInfo}; use crate::abstract_syntax_tree::ast::FillMeta; use std::collections::HashSet; use crate::error_definition::Report; @@ -16,8 +17,10 @@ pub struct ProgramArchive { pub file_library: FileLibrary, pub functions: FunctionInfo, pub templates: TemplateInfo, + pub buses: BusInfo, pub function_keys: HashSet, pub template_keys: HashSet, + pub bus_keys: HashSet, pub public_inputs: Vec, pub initial_template_call: Expression, pub custom_gates: bool, @@ -37,15 +40,20 @@ impl ProgramArchive { reports.append(&mut errs); } } - let (mut fresh_id, functions, templates) = merger.decompose(); + let (mut fresh_id, functions, templates, buses) = merger.decompose(); let mut function_keys = HashSet::new(); let mut template_keys = HashSet::new(); + let mut bus_keys = HashSet::new(); + for key in functions.keys() { function_keys.insert(key.clone()); } for key in templates.keys() { template_keys.insert(key.clone()); } + for key in buses.keys() { + bus_keys.insert(key.clone()); + } let (public_inputs, mut initial_template_call) = main_component; initial_template_call.fill(file_id_main, &mut fresh_id); if reports.is_empty() { @@ -55,10 +63,12 @@ impl ProgramArchive { file_library, functions, templates, + buses, public_inputs, initial_template_call, function_keys, template_keys, + bus_keys, custom_gates, }) } else { @@ -123,6 +133,32 @@ impl ProgramArchive { self.functions.remove(id); } + // bus functions + pub fn contains_bus(&self, bus_name: &str) -> bool { + self.get_buses().contains_key(bus_name) + } + pub fn get_bus_data(&self, bus_name: &str) -> &BusData { + assert!(self.contains_bus(bus_name)); + self.get_buses().get(bus_name).unwrap() + } + pub fn get_mut_bus_data(&mut self, bus_name: &str) -> &mut BusData { + assert!(self.contains_bus(bus_name)); + self.buses.get_mut(bus_name).unwrap() + } + pub fn get_bus_names(&self) -> &HashSet { + &self.bus_keys + } + pub fn get_buses(&self) -> &BusInfo { + &self.buses + } + pub fn get_mut_buses(&mut self) -> &mut BusInfo { + &mut self.buses + } + pub fn remove_bus(&mut self, id: &str) { + self.bus_keys.remove(id); + self.buses.remove(id); + } + //main_component functions pub fn get_public_inputs_main_component(&self) -> &Vec { &self.public_inputs @@ -134,4 +170,4 @@ impl ProgramArchive { pub fn get_file_library(&self) -> &FileLibrary { &self.file_library } -} +} \ No newline at end of file diff --git a/program_structure/src/program_library/program_merger.rs b/program_structure/src/program_library/program_merger.rs index 7e44cac09..5d2d808e8 100644 --- a/program_structure/src/program_library/program_merger.rs +++ b/program_structure/src/program_library/program_merger.rs @@ -4,11 +4,13 @@ use super::error_definition::Report; use super::file_definition::FileID; use super::function_data::{FunctionData, FunctionInfo}; use super::template_data::{TemplateData, TemplateInfo}; +use super::bus_data::{BusData, BusInfo}; pub struct Merger { fresh_id: usize, function_info: FunctionInfo, template_info: TemplateInfo, + bus_info: BusInfo, } impl Default for Merger { fn default() -> Self { @@ -16,6 +18,7 @@ impl Default for Merger { fresh_id: 0, function_info: FunctionInfo::new(), template_info: TemplateInfo::new(), + bus_info: BusInfo::new() } } } @@ -30,7 +33,7 @@ impl Merger { for definition in definitions { let (name, meta) = match definition { Definition::Template { name, args, arg_location, body, meta, parallel, is_custom_gate } => { - if self.contains_function(&name) || self.contains_template(&name) { + if self.contains_function(&name) || self.contains_template(&name) || self.contains_bus(&name) { (Option::Some(name), meta) } else { let new_data = TemplateData::new( @@ -49,7 +52,7 @@ impl Merger { } } Definition::Function { name, body, args, arg_location, meta } => { - if self.contains_function(&name) || self.contains_template(&name) { + if self.contains_function(&name) || self.contains_template(&name) || self.contains_bus(&name) { (Option::Some(name), meta) } else { let new_data = FunctionData::new( @@ -65,6 +68,23 @@ impl Merger { (Option::None, meta) } } + Definition::Bus { name, body, args, arg_location, meta } => { + if self.contains_function(&name) || self.contains_template(&name) || self.contains_bus(&name) { + (Option::Some(name), meta) + } else { + let new_data = BusData::new( + file_id, + name.clone(), + body, + args.len(), + args, + arg_location, + &mut self.fresh_id, + ); + self.get_mut_bus_info().insert(name.clone(), new_data); + (Option::None, meta) + } + } }; if let Option::Some(definition_name) = name { let mut report = Report::error( @@ -101,8 +121,18 @@ impl Merger { &mut self.template_info } + pub fn contains_bus(&self, bus_name: &str) -> bool { + self.get_bus_info().contains_key(bus_name) + } + fn get_bus_info(&self) -> &BusInfo { + &self.bus_info + } + fn get_mut_bus_info(&mut self) -> &mut BusInfo { + &mut self.bus_info + } - pub fn decompose(self) -> (usize, FunctionInfo, TemplateInfo) { - (self.fresh_id, self.function_info, self.template_info) + + pub fn decompose(self) -> (usize, FunctionInfo, TemplateInfo, BusInfo) { + (self.fresh_id, self.function_info, self.template_info, self.bus_info) } -} +} \ No newline at end of file diff --git a/program_structure/src/program_library/template_data.rs b/program_structure/src/program_library/template_data.rs index 125bf7ab8..c4b343d90 100644 --- a/program_structure/src/program_library/template_data.rs +++ b/program_structure/src/program_library/template_data.rs @@ -1,13 +1,10 @@ use super::ast; use super::ast::{FillMeta, Statement}; -use super::file_definition::FileID; -use crate::file_definition::FileLocation; -use std::collections::{HashMap, HashSet, BTreeMap}; +use super::file_definition::{FileID, FileLocation}; +use super::wire_data::*; +use std::collections::{HashMap}; pub type TemplateInfo = HashMap; -pub type TagInfo = HashSet; -type SignalInfo = BTreeMap; -type SignalDeclarationOrder = Vec<(String, usize)>; #[derive(Clone)] pub struct TemplateData { @@ -17,13 +14,13 @@ pub struct TemplateData { num_of_params: usize, name_of_params: Vec, param_location: FileLocation, - input_signals: SignalInfo, - output_signals: SignalInfo, + input_wires: WireInfo, + output_wires: WireInfo, is_parallel: bool, is_custom_gate: bool, /* Only used to know the order in which signals are declared.*/ - input_declarations: SignalDeclarationOrder, - output_declarations: SignalDeclarationOrder, + input_declarations: WireDeclarationOrder, + output_declarations: WireDeclarationOrder, } impl TemplateData { @@ -39,11 +36,11 @@ impl TemplateData { is_custom_gate: bool, ) -> TemplateData { body.fill(file_id, elem_id); - let mut input_signals = SignalInfo::new(); - let mut output_signals = SignalInfo::new(); - let mut input_declarations = SignalDeclarationOrder::new(); - let mut output_declarations = SignalDeclarationOrder::new(); - fill_inputs_and_outputs(&body, &mut input_signals, &mut output_signals, &mut input_declarations, &mut output_declarations); + let mut input_wires = WireInfo::new(); + let mut output_wires = WireInfo::new(); + let mut input_declarations = WireDeclarationOrder::new(); + let mut output_declarations = WireDeclarationOrder::new(); + fill_inputs_and_outputs(&body, &mut input_wires, &mut output_wires, &mut input_declarations, &mut output_declarations); TemplateData { name, file_id, @@ -51,8 +48,8 @@ impl TemplateData { num_of_params, name_of_params, param_location, - input_signals, - output_signals, + input_wires, + output_wires, is_parallel, is_custom_gate, input_declarations, @@ -67,12 +64,12 @@ impl TemplateData { num_of_params: usize, name_of_params: Vec, param_location: FileLocation, - input_signals: SignalInfo, - output_signals: SignalInfo, + input_wires: WireInfo, + output_wires: WireInfo, is_parallel: bool, is_custom_gate: bool, - input_declarations :SignalDeclarationOrder, - output_declarations : SignalDeclarationOrder + input_declarations: WireDeclarationOrder, + output_declarations: WireDeclarationOrder ) -> TemplateData { TemplateData { name, @@ -81,8 +78,8 @@ impl TemplateData { num_of_params, name_of_params, param_location, - input_signals, - output_signals, + input_wires, + output_wires, is_parallel, is_custom_gate, input_declarations, @@ -123,22 +120,22 @@ impl TemplateData { pub fn get_name_of_params(&self) -> &Vec { &self.name_of_params } - pub fn get_input_info(&self, name: &str) -> Option<&(usize, TagInfo)> { - self.input_signals.get(name) + pub fn get_input_info(&self, name: &str) -> Option<&WireData> { + self.input_wires.get(name) } - pub fn get_output_info(&self, name: &str) -> Option<&(usize, TagInfo)> { - self.output_signals.get(name) + pub fn get_output_info(&self, name: &str) -> Option<&WireData> { + self.output_wires.get(name) } - pub fn get_inputs(&self) -> &SignalInfo { - &self.input_signals + pub fn get_inputs(&self) -> &WireInfo { + &self.input_wires } - pub fn get_outputs(&self) -> &SignalInfo { - &self.output_signals + pub fn get_outputs(&self) -> &WireInfo { + &self.output_wires } - pub fn get_declaration_inputs(&self) -> &SignalDeclarationOrder { - &&self.input_declarations + pub fn get_declaration_inputs(&self) -> &WireDeclarationOrder { + &self.input_declarations } - pub fn get_declaration_outputs(&self) -> &SignalDeclarationOrder { + pub fn get_declaration_outputs(&self) -> &WireDeclarationOrder { &self.output_declarations } pub fn get_name(&self) -> &str { @@ -154,51 +151,78 @@ impl TemplateData { fn fill_inputs_and_outputs( template_statement: &Statement, - input_signals: &mut SignalInfo, - output_signals: &mut SignalInfo, - input_declarations : &mut SignalDeclarationOrder, - output_declarations : &mut SignalDeclarationOrder + input_wires: &mut WireInfo, + output_wires: &mut WireInfo, + input_declarations: &mut WireDeclarationOrder, + output_declarations: &mut WireDeclarationOrder ) { + use Statement::*; match template_statement { - Statement::IfThenElse { if_case, else_case, .. } => { - fill_inputs_and_outputs(if_case, input_signals, output_signals, input_declarations, output_declarations); + IfThenElse { if_case, else_case, .. } => { + fill_inputs_and_outputs(if_case, input_wires, output_wires, input_declarations, output_declarations); if let Option::Some(else_value) = else_case { - fill_inputs_and_outputs(else_value, input_signals, output_signals, input_declarations, output_declarations); + fill_inputs_and_outputs(else_value, input_wires, output_wires, input_declarations, output_declarations); } } - Statement::Block { stmts, .. } => { + Block { stmts, .. } => { for stmt in stmts.iter() { - fill_inputs_and_outputs(stmt, input_signals, output_signals, input_declarations, output_declarations); + fill_inputs_and_outputs(stmt, input_wires, output_wires, input_declarations, output_declarations); } } - Statement::While { stmt, .. } => { - fill_inputs_and_outputs(stmt, input_signals, output_signals, input_declarations, output_declarations); + While { stmt, .. } => { + fill_inputs_and_outputs(stmt, input_wires, output_wires, input_declarations, output_declarations); } - Statement::InitializationBlock { initializations, .. } => { + InitializationBlock { initializations, .. } => { for initialization in initializations.iter() { - fill_inputs_and_outputs(initialization, input_signals, output_signals, input_declarations, output_declarations); + fill_inputs_and_outputs(initialization, input_wires, output_wires, input_declarations, output_declarations); } } - Statement::Declaration { xtype, name, dimensions, .. } => { - if let ast::VariableType::Signal(stype, tag_list) = xtype { - let signal_name = name.clone(); - let dim = dimensions.len(); - let mut tag_info = HashSet::new(); - for tag in tag_list{ - tag_info.insert(tag.clone()); - } + Declaration { xtype, name, dimensions, .. } => { + match xtype { + ast::VariableType::Signal(stype, tag_list) => { + let wire_name = name.clone(); + let dim = dimensions.len(); + let mut tag_info = TagInfo::new(); + for tag in tag_list{ + tag_info.insert(tag.clone()); + } + let wire_data = WireData::new(WireType::Signal,dim,tag_info); - match stype { - ast::SignalType::Input => { - input_signals.insert(signal_name.clone(), (dim, tag_info)); - input_declarations.push((signal_name,dim)); + match stype { + ast::SignalType::Input => { + input_wires.insert(wire_name.clone(), wire_data); + input_declarations.push((wire_name,dim)); + } + ast::SignalType::Output => { + output_wires.insert(wire_name.clone(), wire_data); + output_declarations.push((wire_name,dim)); + } + _ => {} //no need to deal with intermediate signals + } + }, + ast::VariableType::Bus(tname, stype, tag_list) => { + let wire_name = name.clone(); + let dim = dimensions.len(); + let type_name = tname.clone(); + let mut tag_info = TagInfo::new(); + for tag in tag_list{ + tag_info.insert(tag.clone()); } - ast::SignalType::Output => { - output_signals.insert(signal_name.clone(), (dim, tag_info)); - output_declarations.push((signal_name,dim)); + let wire_data = WireData::new(WireType::Bus(type_name),dim,tag_info); + + match stype { + ast::SignalType::Input => { + input_wires.insert(wire_name.clone(), wire_data); + input_declarations.push((wire_name,dim)); + } + ast::SignalType::Output => { + output_wires.insert(wire_name.clone(), wire_data); + output_declarations.push((wire_name,dim)); + } + _ => {} //no need to deal with intermediate signals } - _ => {} //no need to deal with intermediate signals - } + }, + _ => {}, } } _ => {} diff --git a/program_structure/src/program_library/wire_data.rs b/program_structure/src/program_library/wire_data.rs new file mode 100644 index 000000000..8e539a89f --- /dev/null +++ b/program_structure/src/program_library/wire_data.rs @@ -0,0 +1,47 @@ +use std::collections::{HashSet, HashMap}; + + +pub type TagInfo = HashSet; +pub type WireInfo = HashMap; +pub type WireDeclarationOrder = Vec<(String, usize)>; + + +#[derive(Clone, PartialEq, Eq)] +pub enum WireType { + Signal, + Bus(String), +} + + +#[derive(Clone)] +pub struct WireData { + wire_type: WireType, + dimension: usize, + tag_info: TagInfo, +} + +impl WireData { + pub fn new( + wire_type: WireType, + dimension: usize, + tag_info: TagInfo, + ) -> WireData { + WireData { + wire_type, + dimension, + tag_info + } + } + pub fn get_type(&self) -> WireType { + self.wire_type.clone() + } + pub fn get_dimension(&self) -> usize { + self.dimension + } + pub fn contains_tag(&self, name: &str) -> bool { + self.tag_info.contains(name) + } + pub fn get_tags(&self) -> &TagInfo { + &self.tag_info + } +} \ No newline at end of file diff --git a/program_structure/src/utils/environment.rs b/program_structure/src/utils/environment.rs index 0215dce61..cd181ed48 100644 --- a/program_structure/src/utils/environment.rs +++ b/program_structure/src/utils/environment.rs @@ -4,6 +4,7 @@ use std::marker::PhantomData; pub trait VarInfo {} pub trait SignalInfo {} +pub trait BusInfo {} pub trait ComponentInfo {} #[derive(Clone)] @@ -13,67 +14,84 @@ impl VarInfo for OnlyVars {} pub struct OnlySignals; impl SignalInfo for OnlySignals {} #[derive(Clone)] +pub struct OnlyBuses; +impl BusInfo for OnlyBuses {} +#[derive(Clone)] pub struct OnlyComponents; impl ComponentInfo for OnlyComponents {} #[derive(Clone)] pub struct FullEnvironment; impl VarInfo for FullEnvironment {} impl SignalInfo for FullEnvironment {} +impl BusInfo for FullEnvironment {} impl ComponentInfo for FullEnvironment {} -pub type VarEnvironment = RawEnvironment; -pub type SignalEnvironment = RawEnvironment; -pub type ComponentEnvironment = RawEnvironment; -pub type CircomEnvironment = RawEnvironment; +pub type VarEnvironment = RawEnvironment; +pub type SignalEnvironment = RawEnvironment; +pub type BusEnvironment = RawEnvironment; +pub type ComponentEnvironment = RawEnvironment; +pub type CircomEnvironment = RawEnvironment; pub enum CircomEnvironmentError { NonExistentSymbol, } #[derive(Clone)] -pub struct RawEnvironment { - components: HashMap, - inputs: HashMap, - outputs: HashMap, - intermediates: HashMap, +pub struct RawEnvironment { variables: Vec>, + signal_inputs: HashMap, + signal_outputs: HashMap, + signal_intermediates: HashMap, + bus_inputs: HashMap, + bus_outputs: HashMap, + bus_intermediates: HashMap, + components: HashMap, behaviour: PhantomData, } -impl Default for RawEnvironment { +impl Default for RawEnvironment { fn default() -> Self { let variables = vec![VariableBlock::new()]; RawEnvironment { - components: HashMap::new(), - inputs: HashMap::new(), - outputs: HashMap::new(), - intermediates: HashMap::new(), variables, + signal_inputs: HashMap::new(), + signal_outputs: HashMap::new(), + signal_intermediates: HashMap::new(), + bus_inputs: HashMap::new(), + bus_outputs: HashMap::new(), + bus_intermediates: HashMap::new(), + components: HashMap::new(), behaviour: PhantomData, } } } -impl RawEnvironment +impl RawEnvironment where - T: VarInfo + SignalInfo + ComponentInfo, + T: VarInfo + SignalInfo + BusInfo + ComponentInfo, { pub fn has_symbol(&self, symbol: &str) -> bool { - self.has_signal(symbol) || self.has_component(symbol) || self.has_variable(symbol) + self.has_signal(symbol) || self.has_bus(symbol) || self.has_component(symbol) || self.has_variable(symbol) } } -impl RawEnvironment { +impl RawEnvironment { pub fn merge( - left: RawEnvironment, - right: RawEnvironment, + left: RawEnvironment, + right: RawEnvironment, using: fn(VC, VC) -> VC, - ) -> RawEnvironment { + ) -> RawEnvironment { + let mut signal_inputs = left.signal_inputs; + let mut signal_outputs = left.signal_outputs; + let mut signal_intermediates = left.signal_intermediates; + let mut bus_inputs = left.bus_inputs; + let mut bus_outputs = left.bus_outputs; + let mut bus_intermediates = left.bus_intermediates; let mut components = left.components; - let mut inputs = left.inputs; - let mut outputs = left.outputs; - let mut intermediates = left.intermediates; + signal_inputs.extend(right.signal_inputs); + signal_outputs.extend(right.signal_outputs); + signal_intermediates.extend(right.signal_intermediates); + bus_inputs.extend(right.bus_inputs); + bus_outputs.extend(right.bus_outputs); + bus_intermediates.extend(right.bus_intermediates); components.extend(right.components); - inputs.extend(right.inputs); - outputs.extend(right.outputs); - intermediates.extend(right.intermediates); let mut variables_left = left.variables; let mut variables_right = right.variables; let mut variables = Vec::new(); @@ -85,16 +103,19 @@ impl RawEnvironment { } variables.reverse(); RawEnvironment { - components, - inputs, - intermediates, - outputs, variables, + signal_inputs, + signal_outputs, + signal_intermediates, + bus_inputs, + bus_outputs, + bus_intermediates, + components, behaviour: PhantomData, } } } -impl RawEnvironment +impl RawEnvironment where T: VarInfo, { @@ -120,7 +141,7 @@ where } Option::None } - pub fn new() -> RawEnvironment { + pub fn new() -> RawEnvironment { RawEnvironment::default() } pub fn add_variable_block(&mut self) { @@ -198,7 +219,7 @@ where } } -impl RawEnvironment +impl RawEnvironment where T: ComponentInfo, { @@ -239,100 +260,100 @@ where } } -impl RawEnvironment +impl RawEnvironment where T: SignalInfo, { pub fn add_input(&mut self, input_name: &str, content: SC) { - self.inputs.insert(input_name.to_string(), content); + self.signal_inputs.insert(input_name.to_string(), content); } pub fn remove_input(&mut self, input_name: &str) { - self.inputs.remove(input_name); + self.signal_inputs.remove(input_name); } pub fn add_output(&mut self, output_name: &str, content: SC) { - self.outputs.insert(output_name.to_string(), content); + self.signal_outputs.insert(output_name.to_string(), content); } pub fn remove_output(&mut self, output_name: &str) { - self.outputs.remove(output_name); + self.signal_outputs.remove(output_name); } pub fn add_intermediate(&mut self, intermediate_name: &str, content: SC) { - self.intermediates.insert(intermediate_name.to_string(), content); + self.signal_intermediates.insert(intermediate_name.to_string(), content); } pub fn remove_intermediate(&mut self, intermediate_name: &str) { - self.intermediates.remove(intermediate_name); + self.signal_intermediates.remove(intermediate_name); } pub fn has_input(&self, symbol: &str) -> bool { - self.inputs.contains_key(symbol) + self.signal_inputs.contains_key(symbol) } pub fn has_output(&self, symbol: &str) -> bool { - self.outputs.contains_key(symbol) + self.signal_outputs.contains_key(symbol) } pub fn has_intermediate(&self, symbol: &str) -> bool { - self.intermediates.contains_key(symbol) + self.signal_intermediates.contains_key(symbol) } pub fn has_signal(&self, symbol: &str) -> bool { self.has_input(symbol) || self.has_output(symbol) || self.has_intermediate(symbol) } pub fn get_input(&self, symbol: &str) -> Option<&SC> { - self.inputs.get(symbol) + self.signal_inputs.get(symbol) } pub fn get_mut_input(&mut self, symbol: &str) -> Option<&mut SC> { - self.inputs.get_mut(symbol) + self.signal_inputs.get_mut(symbol) } pub fn get_input_res(&self, symbol: &str) -> Result<&SC, CircomEnvironmentError> { - self.inputs.get(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol) + self.signal_inputs.get(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol) } pub fn get_input_or_break(&self, symbol: &str, file: &str, line: u32) -> &SC { assert!(self.has_input(symbol), "Method call in file {} line {}", file, line); - self.inputs.get(symbol).unwrap() + self.signal_inputs.get(symbol).unwrap() } pub fn get_mut_input_res(&mut self, symbol: &str) -> Result<&mut SC, CircomEnvironmentError> { - self.inputs.get_mut(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol) + self.signal_inputs.get_mut(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol) } pub fn get_mut_input_or_break(&mut self, symbol: &str, file: &str, line: u32) -> &mut SC { assert!(self.has_input(symbol), "Method call in file {} line {}", file, line); - self.inputs.get_mut(symbol).unwrap() + self.signal_inputs.get_mut(symbol).unwrap() } pub fn get_output(&self, symbol: &str) -> Option<&SC> { - self.outputs.get(symbol) + self.signal_outputs.get(symbol) } pub fn get_mut_output(&mut self, symbol: &str) -> Option<&mut SC> { - self.outputs.get_mut(symbol) + self.signal_outputs.get_mut(symbol) } pub fn get_output_res(&self, symbol: &str) -> Result<&SC, CircomEnvironmentError> { - self.outputs.get(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol) + self.signal_outputs.get(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol) } pub fn get_output_or_break(&self, symbol: &str, file: &str, line: u32) -> &SC { assert!(self.has_output(symbol), "Method call in file {} line {}", file, line); - self.outputs.get(symbol).unwrap() + self.signal_outputs.get(symbol).unwrap() } pub fn get_mut_output_res(&mut self, symbol: &str) -> Result<&mut SC, CircomEnvironmentError> { - self.outputs.get_mut(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol) + self.signal_outputs.get_mut(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol) } pub fn get_mut_output_or_break(&mut self, symbol: &str, file: &str, line: u32) -> &mut SC { assert!(self.has_output(symbol), "Method call in file {} line {}", file, line); - self.outputs.get_mut(symbol).unwrap() + self.signal_outputs.get_mut(symbol).unwrap() } pub fn get_intermediate(&self, symbol: &str) -> Option<&SC> { - self.intermediates.get(symbol) + self.signal_intermediates.get(symbol) } pub fn get_mut_intermediate(&mut self, symbol: &str) -> Option<&mut SC> { - self.intermediates.get_mut(symbol) + self.signal_intermediates.get_mut(symbol) } pub fn get_intermediate_res(&self, symbol: &str) -> Result<&SC, CircomEnvironmentError> { - self.intermediates.get(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol) + self.signal_intermediates.get(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol) } pub fn get_intermediate_or_break(&self, symbol: &str, file: &str, line: u32) -> &SC { assert!(self.has_intermediate(symbol), "Method call in file {} line {}", file, line); - self.intermediates.get(symbol).unwrap() + self.signal_intermediates.get(symbol).unwrap() } pub fn get_mut_intermediate_res( &mut self, symbol: &str, ) -> Result<&mut SC, CircomEnvironmentError> { - self.intermediates.get_mut(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol) + self.signal_intermediates.get_mut(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol) } pub fn get_mut_intermediate_or_break( &mut self, @@ -341,7 +362,7 @@ where line: u32, ) -> &mut SC { assert!(self.has_intermediate(symbol), "Method call in file {} line {}", file, line); - self.intermediates.get_mut(symbol).unwrap() + self.signal_intermediates.get_mut(symbol).unwrap() } pub fn get_signal(&self, symbol: &str) -> Option<&SC> { @@ -406,6 +427,173 @@ where } } +impl RawEnvironment +where + T: BusInfo, +{ + pub fn add_input_bus(&mut self, input_name: &str, content: BC) { + self.bus_inputs.insert(input_name.to_string(), content); + } + pub fn remove_input_bus(&mut self, input_name: &str) { + self.bus_inputs.remove(input_name); + } + pub fn add_output_bus(&mut self, output_name: &str, content: BC) { + self.bus_outputs.insert(output_name.to_string(), content); + } + pub fn remove_output_bus(&mut self, output_name: &str) { + self.bus_outputs.remove(output_name); + } + pub fn add_intermediate_bus(&mut self, intermediate_name: &str, content: BC) { + self.bus_intermediates.insert(intermediate_name.to_string(), content); + } + pub fn remove_intermediate_bus(&mut self, intermediate_name: &str) { + self.bus_intermediates.remove(intermediate_name); + } + pub fn has_input_bus(&self, symbol: &str) -> bool { + self.bus_inputs.contains_key(symbol) + } + pub fn has_output_bus(&self, symbol: &str) -> bool { + self.bus_outputs.contains_key(symbol) + } + pub fn has_intermediate_bus(&self, symbol: &str) -> bool { + self.bus_intermediates.contains_key(symbol) + } + pub fn has_bus(&self, symbol: &str) -> bool { + self.has_input_bus(symbol) || self.has_output_bus(symbol) || self.has_intermediate_bus(symbol) + } + pub fn get_input_bus(&self, symbol: &str) -> Option<&BC> { + self.bus_inputs.get(symbol) + } + pub fn get_mut_input_bus(&mut self, symbol: &str) -> Option<&mut BC> { + self.bus_inputs.get_mut(symbol) + } + pub fn get_input_bus_res(&self, symbol: &str) -> Result<&BC, CircomEnvironmentError> { + self.bus_inputs.get(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol) + } + pub fn get_input_bus_or_break(&self, symbol: &str, file: &str, line: u32) -> &BC { + assert!(self.has_input_bus(symbol), "Method call in file {} line {}", file, line); + self.bus_inputs.get(symbol).unwrap() + } + pub fn get_mut_input_bus_res(&mut self, symbol: &str) -> Result<&mut BC, CircomEnvironmentError> { + self.bus_inputs.get_mut(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol) + } + pub fn get_mut_input_bus_or_break(&mut self, symbol: &str, file: &str, line: u32) -> &mut BC { + assert!(self.has_input_bus(symbol), "Method call in file {} line {}", file, line); + self.bus_inputs.get_mut(symbol).unwrap() + } + + pub fn get_output_bus(&self, symbol: &str) -> Option<&BC> { + self.bus_outputs.get(symbol) + } + pub fn get_mut_output_bus(&mut self, symbol: &str) -> Option<&mut BC> { + self.bus_outputs.get_mut(symbol) + } + pub fn get_output_bus_res(&self, symbol: &str) -> Result<&BC, CircomEnvironmentError> { + self.bus_outputs.get(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol) + } + pub fn get_output_bus_or_break(&self, symbol: &str, file: &str, line: u32) -> &BC { + assert!(self.has_output_bus(symbol), "Method call in file {} line {}", file, line); + self.bus_outputs.get(symbol).unwrap() + } + pub fn get_mut_output_bus_res(&mut self, symbol: &str) -> Result<&mut BC, CircomEnvironmentError> { + self.bus_outputs.get_mut(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol) + } + pub fn get_mut_output_bus_or_break(&mut self, symbol: &str, file: &str, line: u32) -> &mut BC { + assert!(self.has_output_bus(symbol), "Method call in file {} line {}", file, line); + self.bus_outputs.get_mut(symbol).unwrap() + } + + pub fn get_intermediate_bus(&self, symbol: &str) -> Option<&BC> { + self.bus_intermediates.get(symbol) + } + pub fn get_mut_intermediate_bus(&mut self, symbol: &str) -> Option<&mut BC> { + self.bus_intermediates.get_mut(symbol) + } + pub fn get_intermediate_bus_res(&self, symbol: &str) -> Result<&BC, CircomEnvironmentError> { + self.bus_intermediates.get(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol) + } + pub fn get_intermediate_bus_or_break(&self, symbol: &str, file: &str, line: u32) -> &BC { + assert!(self.has_intermediate_bus(symbol), "Method call in file {} line {}", file, line); + self.bus_intermediates.get(symbol).unwrap() + } + pub fn get_mut_intermediate_bus_res( + &mut self, + symbol: &str, + ) -> Result<&mut BC, CircomEnvironmentError> { + self.bus_intermediates.get_mut(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol) + } + pub fn get_mut_intermediate_bus_or_break( + &mut self, + symbol: &str, + file: &str, + line: u32, + ) -> &mut BC { + assert!(self.has_intermediate_bus(symbol), "Method call in file {} line {}", file, line); + self.bus_intermediates.get_mut(symbol).unwrap() + } + + pub fn get_bus(&self, symbol: &str) -> Option<&BC> { + if self.has_input_bus(symbol) { + self.get_input_bus(symbol) + } else if self.has_output_bus(symbol) { + self.get_output_bus(symbol) + } else if self.has_intermediate_bus(symbol) { + self.get_intermediate_bus(symbol) + } else { + Option::None + } + } + pub fn get_mut_bus(&mut self, symbol: &str) -> Option<&mut BC> { + if self.has_input_bus(symbol) { + self.get_mut_input_bus(symbol) + } else if self.has_output_bus(symbol) { + self.get_mut_output_bus(symbol) + } else if self.has_intermediate_bus(symbol) { + self.get_mut_intermediate_bus(symbol) + } else { + Option::None + } + } + pub fn get_bus_res(&self, symbol: &str) -> Result<&BC, CircomEnvironmentError> { + if self.has_input_bus(symbol) { + self.get_input_bus_res(symbol) + } else if self.has_output_bus(symbol) { + self.get_output_bus_res(symbol) + } else if self.has_intermediate_bus(symbol) { + self.get_intermediate_bus_res(symbol) + } else { + Result::Err(CircomEnvironmentError::NonExistentSymbol) + } + } + pub fn get_bus_or_break(&self, symbol: &str, file: &str, line: u32) -> &BC { + assert!(self.has_bus(symbol), "Method call in file {} line {}", file, line); + if let Result::Ok(v) = self.get_bus_res(symbol) { + v + } else { + unreachable!(); + } + } + pub fn get_mut_bus_res(&mut self, symbol: &str) -> Result<&mut BC, CircomEnvironmentError> { + if self.has_input_bus(symbol) { + self.get_mut_input_bus_res(symbol) + } else if self.has_output_bus(symbol) { + self.get_mut_output_bus_res(symbol) + } else if self.has_intermediate_bus(symbol) { + self.get_mut_intermediate_bus_res(symbol) + } else { + Result::Err(CircomEnvironmentError::NonExistentSymbol) + } + } + pub fn get_mut_bus_or_break(&mut self, symbol: &str, file: &str, line: u32) -> &mut BC { + assert!(self.has_bus(symbol), "Method call in file {} line {}", file, line); + if let Result::Ok(v) = self.get_mut_bus_res(symbol) { + v + } else { + unreachable!(); + } + } +} + #[derive(Clone)] struct VariableBlock { variables: HashMap, diff --git a/program_structure/src/utils/memory_slice.rs b/program_structure/src/utils/memory_slice.rs index 76a8e7d9e..c918a1ba6 100644 --- a/program_structure/src/utils/memory_slice.rs +++ b/program_structure/src/utils/memory_slice.rs @@ -6,12 +6,16 @@ pub enum TypeInvalidAccess { MissingInputTags(String), NoInitializedComponent, NoInitializedSignal, + NoInitializedBus, } pub enum TypeAssignmentError { + MultipleAssignmentsComponent, + MultipleAssignmentsBus, MultipleAssignments, AssignmentOutput, NoInitializedComponent, + DifferentBusInstances, AssignmentInput(String), } @@ -20,6 +24,7 @@ pub enum MemoryError { AssignmentError(TypeAssignmentError), InvalidAccess(TypeInvalidAccess), UnknownSizeDimension, + MismatchedInstances, MismatchedDimensions(usize, usize), MismatchedDimensionsWeak(usize, usize), AssignmentMissingTags(String, String), @@ -37,13 +42,18 @@ pub type SimpleSlice = MemorySlice; The attribute route stores the dimensions of the slice, used to navigate through them. The length of values is equal to multiplying all the values in route. */ -#[derive(Eq, PartialEq)] pub struct MemorySlice { route: Vec, values: Vec, number_inserts: usize, } +impl PartialEq for MemorySlice { + fn eq(&self, other: &Self) -> bool{ + self.route == other.route && self.values == other.values + } +} + impl Clone for MemorySlice { fn clone(&self) -> Self { MemorySlice { @@ -134,10 +144,19 @@ impl MemorySlice { } if new_values.route[i] > memory_slice.route[initial_index_new + i] { - return Result::Err(MemoryError::MismatchedDimensions( - new_values.route[i], - memory_slice.route[initial_index_new + i], - )); + if is_strict{ + return Result::Err(MemoryError::MismatchedDimensions( + new_values.route[i], + memory_slice.route[initial_index_new + i], + )); + } + else{ + // case variables: we allow the assignment of smaller arrays + return Result::Err(MemoryError::MismatchedDimensionsWeak( + new_values.route[i], + memory_slice.route[initial_index_new + i], + )); + } } i += 1; } @@ -185,6 +204,62 @@ impl MemorySlice { Result::Ok(MemorySlice { route: size, values, number_inserts: 0 }) } + fn generate_references_from_access<'a>( + memory_slice: &'a MemorySlice, + access: &[SliceCapacity], + ) -> Result, MemoryError> { + if access.is_empty() { + let mut values = Vec::new(); + for v in &memory_slice.values{ + values.push(v); + } + return Ok(values); + } + + let (_size, number_of_cells) = + MemorySlice::generate_new_route_from_access(memory_slice, access)?; + let mut values = Vec::with_capacity(number_of_cells); + let initial_cell = MemorySlice::get_initial_cell(memory_slice, access)?; + let mut offset = 0; + while offset < number_of_cells { + let new_value = &memory_slice.values[initial_cell + offset]; + values.push(new_value); + offset += 1; + } + + Ok(values) + } + + fn generate_mut_references_from_access<'a>( + memory_slice: &'a mut MemorySlice, + access: &[SliceCapacity], + ) -> Result, MemoryError> { + // TODO: improve, no traverse complete vector + + if access.is_empty() { + let mut values = Vec::new(); + for v in &mut memory_slice.values{ + values.push(v); + } + return Ok(values); + } + + let (_size, number_of_cells) = + MemorySlice::generate_new_route_from_access(memory_slice, access)?; + let mut values = Vec::with_capacity(number_of_cells); + let initial_cell = MemorySlice::get_initial_cell(memory_slice, access)?; + + let mut index = 0; + for v in &mut memory_slice.values{ + if index >= initial_cell && index < initial_cell + number_of_cells{ + values.push(v); + } + index += 1; + } + + Ok(values) + } + // User operations pub fn new(initial_value: &C) -> MemorySlice { MemorySlice::new_with_route(&[], initial_value) @@ -215,11 +290,6 @@ impl MemorySlice { Result::Ok(_) => { let mut cell = MemorySlice::get_initial_cell(memory_slice, access)?; - // if MemorySlice::get_number_of_cells(new_values) - // > (MemorySlice::get_number_of_cells(memory_slice) - cell) - // { - // return Result::Err(MemoryError::OutOfBoundsError); - memory_slice.number_inserts += MemorySlice::get_number_of_cells(new_values); for value in new_values.values.iter() { memory_slice.values[cell] = value.clone(); @@ -229,15 +299,19 @@ impl MemorySlice { } Result::Err(MemoryError::MismatchedDimensionsWeak(dim_1, dim_2)) => { let mut cell = MemorySlice::get_initial_cell(memory_slice, access)?; - // if MemorySlice::get_number_of_cells(new_values) - // > (MemorySlice::get_number_of_cells(memory_slice) - cell) - // { - // return Result::Err(MemoryError::OutOfBoundsError); - // } - for value in new_values.values.iter() { - memory_slice.values[cell] = value.clone(); + + // We assign the min between the number of cells in the new values and the memory slice + let number_inserts = std::cmp::min( + MemorySlice::get_number_of_cells(new_values), + MemorySlice::get_number_of_cells(memory_slice) + ); + + memory_slice.number_inserts += number_inserts; + for i in 0..number_inserts{ + memory_slice.values[cell] = new_values.values[i].clone(); cell += 1; } + Result::Err(MemoryError::MismatchedDimensionsWeak(dim_1, dim_2)) } Result::Err(error) => return Err(error), @@ -282,6 +356,21 @@ impl MemorySlice { ) -> Result, MemoryError> { MemorySlice::generate_slice_from_access(memory_slice, access) } + + pub fn access_values_by_reference<'a>( + memory_slice: &'a MemorySlice, + access: &[SliceCapacity], + ) -> Result, MemoryError> { + MemorySlice::generate_references_from_access(memory_slice, access) + } + + pub fn access_values_by_mut_reference<'a>( + memory_slice: &'a mut MemorySlice, + access: &[SliceCapacity], + ) -> Result, MemoryError> { + MemorySlice::generate_mut_references_from_access(memory_slice, access) + } + pub fn access_value_by_index( memory_slice: &MemorySlice, index: usize, @@ -332,6 +421,15 @@ impl MemorySlice { let cell = MemorySlice::get_initial_cell(memory_slice, access)?; Result::Ok(&mut memory_slice.values[cell]) } + pub fn get_mut_reference_to_single_value_by_index<'a>( + memory_slice: &'a mut MemorySlice, + index: usize, + ) -> Result<&'a mut C, MemoryError> { + if index > MemorySlice::get_number_of_cells(memory_slice) { + return Result::Err(MemoryError::OutOfBoundsError); + } + Result::Ok(&mut memory_slice.values[index]) + } pub fn get_number_of_cells(memory_slice: &MemorySlice) -> SliceCapacity { memory_slice.values.len() } @@ -356,9 +454,11 @@ impl MemorySlice { let mut memory_slice = memory_slice; memory_slice.values.pop().unwrap() } + pub fn destruct(self) -> (Vec, Vec) { (self.route, self.values) } + } #[cfg(test)] diff --git a/type_analysis/Cargo.toml b/type_analysis/Cargo.toml index bccfbdf64..97b7881d3 100644 --- a/type_analysis/Cargo.toml +++ b/type_analysis/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "type_analysis" -version = "2.1.9" +version = "2.2.0" authors = ["Costa Group UCM","iden3"] edition = "2018" diff --git a/type_analysis/src/analyzers/buses_free_of_invalid_statements.rs b/type_analysis/src/analyzers/buses_free_of_invalid_statements.rs new file mode 100644 index 000000000..b5a71dd4f --- /dev/null +++ b/type_analysis/src/analyzers/buses_free_of_invalid_statements.rs @@ -0,0 +1,165 @@ +use program_structure::ast::*; +use program_structure::program_library::bus_data::BusData; +use program_structure::error_code::ReportCode; +use program_structure::error_definition::{Report, ReportCollection}; +use program_structure::file_definition; +use std::collections::HashSet; + +pub fn free_of_invalid_statements( + bus_data: &BusData, + function_names: &HashSet, +) -> Result<(), ReportCollection> { + let body = bus_data.get_body(); + let mut reports = Vec::new(); + analyse_statement(body, function_names, &mut reports); + if reports.is_empty() { + Result::Ok(()) + } else { + Result::Err(reports) + } +} + +fn analyse_statement( + stmt: &Statement, + function_names: &HashSet, + reports: &mut ReportCollection, +) { + use Statement::*; + let file_id = stmt.get_meta().get_file_id(); + match stmt { + MultSubstitution { .. } => unreachable!(), + IfThenElse { meta, .. } => { + report_undefined_bus_error(meta, "Conditional statement used inside the bus",file_id, None, reports); + }, + While { meta, .. } => { + report_undefined_bus_error(meta, "Loop statement used inside the bus", file_id, None, reports); + }, + Block { stmts, .. } => { + for stmt in stmts.iter() { + analyse_statement(stmt, function_names, reports); + } + }, + InitializationBlock { initializations, .. } => { + for stmt in initializations.iter() { + analyse_statement(stmt, function_names, reports); + } + }, + Declaration { meta, xtype, dimensions, .. } => { + match xtype { + VariableType::Signal(stype, _) + | VariableType::Bus(_, stype, _) => { + if *stype == SignalType::Intermediate { + for dimension in dimensions.iter() { + analyse_expression(dimension, function_names, reports); + } + } else { + report_undefined_bus_error(meta, "Template elements declared inside the bus", file_id, None, reports) + } + }, + _ => { + report_undefined_bus_error(meta, "Template elements declared inside the bus", file_id, None, reports) + } + } + }, + Substitution { meta, rhe, access, .. } => { + if !rhe.is_bus_call_array() { + report_undefined_bus_error(meta, "Substitution statement used inside the bus", file_id, None, reports); + } + analyse_access(access, meta, function_names, reports) + } + UnderscoreSubstitution { meta, rhe, .. } => { + if !rhe.is_bus_call_array() { + report_undefined_bus_error(meta, "Substitution statement used inside the bus", file_id, None, reports); + } + } + ConstraintEquality { meta, .. } => { + report_undefined_bus_error(meta, "Constraint statement used inside the bus", file_id, None, reports); + }, + LogCall { meta, .. } => { + report_undefined_bus_error(meta, "I/O statement used inside the bus", file_id, None, reports) + }, + Assert { meta, .. } => { + report_undefined_bus_error(meta, "Assert statement used inside the bus", file_id, None, reports) + }, + Return { meta, .. } => { + report_undefined_bus_error(meta, "Return statement used inside the bus", file_id, None, reports) + }, + } +} + +fn report_undefined_bus_error(meta: &Meta, msg : &str, file_id: usize, primary_msg : Option<&str>, reports: &mut Vec) { + let mut report = Report::error( + msg.to_string(), + ReportCode::UndefinedBus, + ); + let location = + file_definition::generate_file_location(meta.get_start(), meta.get_end()); + let primary_msg = if let Some(msg) = primary_msg { msg } else {"Using invalid statement"}; + report.add_primary(location, file_id, primary_msg.to_string()); + reports.push(report); +} + +fn analyse_expression( + expr: &Expression, + function_names: &HashSet, + reports: &mut ReportCollection, +) { + use Expression::*; + let file_id = expr.get_meta().get_file_id(); + match expr { + InfixOp { lhe, rhe, .. } => { + analyse_expression(lhe, function_names, reports); + analyse_expression(rhe, function_names, reports); + } + PrefixOp { rhe, .. } => { + analyse_expression(rhe, function_names, reports); + } + ParallelOp{ rhe, ..} =>{ + analyse_expression(rhe, function_names, reports); + } + InlineSwitchOp { cond, if_true, if_false, .. } => { + analyse_expression(cond, function_names, reports); + analyse_expression(if_true, function_names, reports); + analyse_expression(if_false, function_names, reports); + } + Variable { meta, access, .. } => analyse_access(access, meta, function_names, reports), + Number(..) => {} + Call { meta, id, args, .. } => { + if !function_names.contains(id) { + report_undefined_bus_error(meta, "Unknown call in bus", file_id, Some("Is not a function call"), reports) + } + for arg in args.iter() { + analyse_expression(arg, function_names, reports); + } + } + ArrayInLine { values, .. } => { + for value in values.iter() { + analyse_expression(value, function_names, reports); + } + } + UniformArray {value, dimension, .. } => { + analyse_expression(value, function_names, reports); + analyse_expression(dimension, function_names, reports); + } + BusCall { meta, .. } => { + report_undefined_bus_error(meta, "Template elements declared inside the bus", file_id, Some("Declaring template element"), reports) + }, + _ => {unreachable!("Anonymous calls should not be reachable at this point."); } + } +} + +fn analyse_access( + access: &Vec, + meta: &Meta, + function_names: &HashSet, + reports: &mut ReportCollection, +) { + let file_id = meta.get_file_id(); + for acc in access.iter() { + if let Access::ArrayAccess(index) = acc { + analyse_expression(index, function_names, reports); + } else { + report_undefined_bus_error(meta, "Bus uses name-access operators", file_id, Some("Template operator found"), reports) + } + } +} \ No newline at end of file diff --git a/type_analysis/src/analyzers/custom_gate_analysis.rs b/type_analysis/src/analyzers/custom_gate_analysis.rs index d829d6d2f..421f411f9 100644 --- a/type_analysis/src/analyzers/custom_gate_analysis.rs +++ b/type_analysis/src/analyzers/custom_gate_analysis.rs @@ -46,7 +46,23 @@ pub fn custom_gate_analysis( ) ); warnings.push(warning); - } + }, + Bus(_,SignalType::Intermediate, _) => { + let mut warning = Report::warning( + String::from("Intermediate bus inside custom template"), + ReportCode::CustomGateIntermediateSignalWarning + ); + warning.add_primary( + meta.location.clone(), + meta.file_id.unwrap(), + format!( + "Intermediate bus {} declared in custom template {}", + name, + custom_gate_name + ) + ); + warnings.push(warning); + }, Component | AnonymousComponent => { let mut error = Report::error( String::from("Component inside custom template"), diff --git a/type_analysis/src/analyzers/functions_free_of_template_elements.rs b/type_analysis/src/analyzers/functions_free_of_template_elements.rs index b5330864c..d8371d214 100644 --- a/type_analysis/src/analyzers/functions_free_of_template_elements.rs +++ b/type_analysis/src/analyzers/functions_free_of_template_elements.rs @@ -205,6 +205,16 @@ fn analyse_expression( } + BusCall { meta, .. } => { + let mut report = Report::error( + "Template elements declared inside the function".to_string(), + ReportCode::UndefinedFunction, + ); + let location = + file_definition::generate_file_location(meta.get_start(), meta.get_end()); + report.add_primary(location, file_id, "Declaring template element".to_string()); + reports.push(report); + }, _ => {unreachable!("Anonymous calls should not be reachable at this point."); } } } diff --git a/type_analysis/src/analyzers/mod.rs b/type_analysis/src/analyzers/mod.rs index bcfc47dee..ea4dc9f8f 100644 --- a/type_analysis/src/analyzers/mod.rs +++ b/type_analysis/src/analyzers/mod.rs @@ -1,3 +1,4 @@ +pub use buses_free_of_invalid_statements::free_of_invalid_statements; pub use custom_gate_analysis::custom_gate_analysis; pub use functions_all_paths_with_return_statement::all_paths_with_return_check; pub use functions_free_of_template_elements::free_of_template_elements; @@ -7,6 +8,7 @@ pub use symbol_analysis::check_naming_correctness; pub use type_check::type_check; pub use unknown_known_analysis::unknown_known_analysis; +pub mod buses_free_of_invalid_statements; pub mod custom_gate_analysis; pub mod functions_all_paths_with_return_statement; pub mod functions_free_of_template_elements; diff --git a/type_analysis/src/analyzers/signal_declaration_analysis.rs b/type_analysis/src/analyzers/signal_declaration_analysis.rs index aa63f7a60..bfba85b00 100644 --- a/type_analysis/src/analyzers/signal_declaration_analysis.rs +++ b/type_analysis/src/analyzers/signal_declaration_analysis.rs @@ -41,10 +41,12 @@ fn treat_statement( } } InitializationBlock { meta, xtype, .. } => match xtype { - VariableType::Signal(_, _) | VariableType::Component => { + VariableType::Signal(_, _) + | VariableType::Bus(_, _, _) + | VariableType::Component => { if !signal_declaration_allowed { let mut report = Report::error( - "Signal or component declaration inside While scope. Signal and component can only be defined in the initial scope or in If scopes with known condition".to_string(), + "Signal, bus or component declaration inside While scope. Signals, buses and components can only be defined in the initial scope or in If scopes with known condition".to_string(), ReportCode::SignalOutsideOriginalScope, ); let location = diff --git a/type_analysis/src/analyzers/symbol_analysis.rs b/type_analysis/src/analyzers/symbol_analysis.rs index 1d92ce25e..94a9ad05e 100644 --- a/type_analysis/src/analyzers/symbol_analysis.rs +++ b/type_analysis/src/analyzers/symbol_analysis.rs @@ -1,10 +1,11 @@ -use program_structure::ast::{Access, Expression, Meta, Statement, LogArgument}; +use program_structure::ast::{Access, Expression, LogArgument, Meta, Statement, VariableType}; use program_structure::error_code::ReportCode; use program_structure::error_definition::{Report, ReportCollection}; use program_structure::file_definition::{self, FileID, FileLocation}; -use program_structure::function_data::FunctionInfo; use program_structure::program_archive::ProgramArchive; +use program_structure::function_data::FunctionInfo; use program_structure::template_data::TemplateInfo; +use program_structure::bus_data::BusInfo; use std::collections::HashSet; type Block = HashSet; type Environment = Vec; @@ -12,6 +13,7 @@ type Environment = Vec; pub fn check_naming_correctness(program_archive: &ProgramArchive) -> Result<(), ReportCollection> { let template_info = program_archive.get_templates(); let function_info = program_archive.get_functions(); + let bus_info = program_archive.get_buses(); let mut reports = ReportCollection::new(); let mut instances = Vec::new(); for (_, data) in template_info { @@ -32,6 +34,15 @@ pub fn check_naming_correctness(program_archive: &ProgramArchive) -> Result<(), ); instances.push(instance); } + for (_, data) in bus_info { + let instance = ( + data.get_file_id(), + data.get_param_location(), + data.get_name_of_params(), + data.get_body_as_vec(), + ); + instances.push(instance); + } if let Err(mut r) = analyze_main(program_archive) { reports.append(&mut r); } @@ -43,6 +54,7 @@ pub fn check_naming_correctness(program_archive: &ProgramArchive) -> Result<(), body, function_info, template_info, + bus_info, ); if let Result::Err(mut r) = res { reports.append(&mut r); @@ -60,6 +72,7 @@ fn analyze_main(program: &ProgramArchive) -> Result<(), Vec> { let signals = program.get_public_inputs_main_component(); let template_info = program.get_templates(); let function_info = program.get_functions(); + let bus_info = program.get_buses(); let mut reports = vec![]; if let Expression::Call { id, .. } = call { @@ -87,6 +100,7 @@ fn analyze_main(program: &ProgramArchive) -> Result<(), Vec> { call.get_meta().get_file_id(), function_info, template_info, + bus_info, &mut reports, &environment, ); @@ -101,6 +115,7 @@ pub fn analyze_symbols( body: &[Statement], function_info: &FunctionInfo, template_info: &TemplateInfo, + bus_info: &BusInfo, ) -> Result<(), ReportCollection> { let mut param_name_collision = false; let mut reports = ReportCollection::new(); @@ -126,6 +141,7 @@ pub fn analyze_symbols( file_id, function_info, template_info, + bus_info, &mut reports, &mut environment, ); @@ -160,19 +176,20 @@ fn analyze_statement( file_id: FileID, function_info: &FunctionInfo, template_info: &TemplateInfo, + bus_info: &BusInfo, reports: &mut ReportCollection, environment: &mut Environment, ) { match stmt { Statement::MultSubstitution { .. } => unreachable!(), Statement::Return { value, .. } => { - analyze_expression(value, file_id, function_info, template_info, reports, environment) + analyze_expression(value, file_id, function_info, template_info, bus_info, reports, environment) } Statement::UnderscoreSubstitution { rhe, .. } => { - analyze_expression(rhe, file_id, function_info, template_info, reports, environment); + analyze_expression(rhe, file_id, function_info, template_info, bus_info, reports, environment); } Statement::Substitution { meta, var, access, rhe, .. } => { - analyze_expression(rhe, file_id, function_info, template_info, reports, environment); + analyze_expression(rhe, file_id, function_info, template_info, bus_info, reports, environment); treat_variable( meta, var, @@ -180,13 +197,14 @@ fn analyze_statement( file_id, function_info, template_info, + bus_info, reports, environment, ); } Statement::ConstraintEquality { lhe, rhe, .. } => { - analyze_expression(lhe, file_id, function_info, template_info, reports, environment); - analyze_expression(rhe, file_id, function_info, template_info, reports, environment); + analyze_expression(lhe, file_id, function_info, template_info, bus_info, reports, environment); + analyze_expression(rhe, file_id, function_info, template_info, bus_info, reports, environment); } Statement::InitializationBlock { initializations, .. } => { for initialization in initializations.iter() { @@ -195,18 +213,20 @@ fn analyze_statement( file_id, function_info, template_info, + bus_info, reports, environment, ); } } - Statement::Declaration { meta, name, dimensions, .. } => { + Statement::Declaration { meta, name, dimensions, xtype, .. } => { for index in dimensions { analyze_expression( index, file_id, function_info, template_info, + bus_info, reports, environment, ); @@ -223,16 +243,34 @@ fn analyze_statement( ); reports.push(report); } + if let VariableType::Bus(b_name,_,tags ) = xtype { + if let Some(bus) = bus_info.get(b_name) { + for t in tags { + if bus.get_fields().contains_key(t) { + let mut report = Report::error( + format!("Declaring same symbol twice"), + ReportCode::SameSymbolDeclaredTwice, + ); + report.add_primary( + meta.location.clone(), + file_id.clone(), + format!("This tag name is also a field name in the bus."), + ); + reports.push(report); + } + } + } + } } Statement::LogCall { args, .. } => { for logarg in args { if let LogArgument::LogExp(arg) = logarg { - analyze_expression(arg, file_id, function_info, template_info, reports, environment); + analyze_expression(arg, file_id, function_info, template_info, bus_info, reports, environment); } } } Statement::Assert { arg, .. } => { - analyze_expression(arg, file_id, function_info, template_info, reports, environment) + analyze_expression(arg, file_id, function_info, template_info, bus_info, reports, environment) } Statement::Block { stmts, .. } => { environment.push(Block::new()); @@ -242,6 +280,7 @@ fn analyze_statement( file_id, function_info, template_info, + bus_info, reports, environment, ); @@ -249,18 +288,19 @@ fn analyze_statement( environment.pop(); } Statement::While { stmt, cond, .. } => { - analyze_expression(cond, file_id, function_info, template_info, reports, environment); - analyze_statement(stmt, file_id, function_info, template_info, reports, environment); + analyze_expression(cond, file_id, function_info, template_info, bus_info, reports, environment); + analyze_statement(stmt, file_id, function_info, template_info, bus_info, reports, environment); } Statement::IfThenElse { cond, if_case, else_case, .. } => { - analyze_expression(cond, file_id, function_info, template_info, reports, environment); - analyze_statement(if_case, file_id, function_info, template_info, reports, environment); + analyze_expression(cond, file_id, function_info, template_info, bus_info, reports, environment); + analyze_statement(if_case, file_id, function_info, template_info, bus_info, reports, environment); if let Option::Some(else_stmt) = else_case { analyze_statement( else_stmt, file_id, function_info, template_info, + bus_info, reports, environment, ); @@ -276,6 +316,7 @@ fn treat_variable( file_id: FileID, function_info: &FunctionInfo, template_info: &TemplateInfo, + bus_info: &BusInfo, reports: &mut ReportCollection, environment: &Environment, ) { @@ -289,8 +330,11 @@ fn treat_variable( reports.push(report); } for acc in access.iter() { - if let Access::ArrayAccess(index) = acc { - analyze_expression(index, file_id, function_info, template_info, reports, environment); + match acc { + Access::ArrayAccess(index) => { + analyze_expression(index, file_id, function_info, template_info, bus_info, reports, environment); + } + Access::ComponentAccess(_) => {} } } } @@ -300,27 +344,29 @@ fn analyze_expression( file_id: FileID, function_info: &FunctionInfo, template_info: &TemplateInfo, + bus_info: &BusInfo, reports: &mut ReportCollection, environment: &Environment, ) { match expression { Expression::InfixOp { lhe, rhe, .. } => { - analyze_expression(lhe, file_id, function_info, template_info, reports, environment); - analyze_expression(rhe, file_id, function_info, template_info, reports, environment); + analyze_expression(lhe, file_id, function_info, template_info, bus_info, reports, environment); + analyze_expression(rhe, file_id, function_info, template_info, bus_info, reports, environment); } Expression::PrefixOp { rhe, .. } => { - analyze_expression(rhe, file_id, function_info, template_info, reports, environment) + analyze_expression(rhe, file_id, function_info, template_info, bus_info, reports, environment) } Expression::ParallelOp { rhe, .. } => { - analyze_expression(rhe, file_id, function_info, template_info, reports, environment) + analyze_expression(rhe, file_id, function_info, template_info, bus_info, reports, environment) } Expression::InlineSwitchOp { cond, if_true, if_false, .. } => { - analyze_expression(cond, file_id, function_info, template_info, reports, environment); + analyze_expression(cond, file_id, function_info, template_info, bus_info, reports, environment); analyze_expression( if_true, file_id, function_info, template_info, + bus_info, reports, environment, ); @@ -329,6 +375,7 @@ fn analyze_expression( file_id, function_info, template_info, + bus_info, reports, environment, ); @@ -340,6 +387,7 @@ fn analyze_expression( file_id, function_info, template_info, + bus_info, reports, environment, ), @@ -379,6 +427,7 @@ fn analyze_expression( file_id, function_info, template_info, + bus_info, reports, environment, ); @@ -391,6 +440,7 @@ fn analyze_expression( file_id, function_info, template_info, + bus_info, reports, environment, ); @@ -402,6 +452,7 @@ fn analyze_expression( file_id, function_info, template_info, + bus_info, reports, environment, ); @@ -410,10 +461,50 @@ fn analyze_expression( file_id, function_info, template_info, + bus_info, reports, environment, ); }, + Expression::BusCall { meta, id, args } => + { + if !bus_info.contains_key(id) { + let mut report = + Report::error(format!("Calling symbol"), ReportCode::NonExistentSymbol); + report.add_primary( + file_definition::generate_file_location(meta.get_start(), meta.get_end()), + file_id.clone(), + format!("Calling unknown symbol"), + ); + reports.push(report); + return; + } + let expected_num_of_params = bus_info.get(id).unwrap().get_num_of_params(); + if args.len() != expected_num_of_params { + let mut report = Report::error( + format!("Instantiating bus with wrong number of arguments"), + ReportCode::BusWrongNumberOfArguments, + ); + report.add_primary( + file_definition::generate_file_location(meta.get_start(), meta.get_end()), + file_id.clone(), + format!("Got {} params, {} where expected", args.len(), expected_num_of_params), + ); + reports.push(report); + return; + } + for arg in args.iter() { + analyze_expression( + arg, + file_id, + function_info, + template_info, + bus_info, + reports, + environment, + ); + } + }, _ => {} } -} +} \ No newline at end of file diff --git a/type_analysis/src/analyzers/type_check.rs b/type_analysis/src/analyzers/type_check.rs index 44bf9ead9..3c708ac3f 100644 --- a/type_analysis/src/analyzers/type_check.rs +++ b/type_analysis/src/analyzers/type_check.rs @@ -1,17 +1,22 @@ -use program_structure::ast::Expression::Call; use super::type_given_function::type_given_function; use super::type_register::TypeRegister; use program_structure::ast::*; +use program_structure::ast::Expression::Call; use program_structure::environment::CircomEnvironment; use program_structure::error_code::ReportCode; use program_structure::error_definition::{Report, ReportCollection}; use program_structure::file_definition::{generate_file_location, FileID}; use program_structure::program_archive::ProgramArchive; +use program_structure::wire_data::WireType; use std::collections::HashSet; + type ArithmeticType = usize; type ComponentInfo = (Option, ArithmeticType); -type TypingEnvironment = CircomEnvironment), ArithmeticType>; +type BusInfo = (Option, ArithmeticType, std::vec::Vec); +type SignalInfo = (ArithmeticType, std::vec::Vec); +type VarInfo = ArithmeticType; +type TypingEnvironment = CircomEnvironment; type CallRegister = TypeRegister; struct AnalysisInformation { @@ -24,19 +29,29 @@ struct AnalysisInformation { } struct FoldedType { + // var dimension arithmetic: Option, + // template name template: Option, + // bus type name + bus: Option, } impl FoldedType { pub fn arithmetic_type(dimensions: ArithmeticType) -> FoldedType { - FoldedType { arithmetic: Option::Some(dimensions), template: Option::None } + FoldedType { arithmetic: Option::Some(dimensions), template: Option::None, bus: Option::None } } pub fn template(name: &str) -> FoldedType { - FoldedType { template: Option::Some(name.to_string()), arithmetic: Option::None } + FoldedType { template: Option::Some(name.to_string()), arithmetic: Option::None, bus: Option::None } } pub fn is_template(&self) -> bool { self.template.is_some() && self.arithmetic.is_none() } + pub fn bus(name: &str, dimensions: ArithmeticType) -> FoldedType { + FoldedType { bus: Option::Some(name.to_string()), arithmetic: Option::Some(dimensions), template: Option::None } + } + pub fn is_bus(&self) -> bool { + self.bus.is_some() + } pub fn dim(&self) -> usize { if let Option::Some(dim) = &self.arithmetic { *dim @@ -46,11 +61,12 @@ impl FoldedType { } pub fn same_type(left: &FoldedType, right: &FoldedType) -> bool { let mut equal = false; - if let (Option::Some(l_template), Option::Some(r_template)) = - (&left.template, &right.template) - { + if let (Option::Some(l_template), Option::Some(r_template)) = (&left.template, &right.template) { equal = l_template.eq(r_template); } + if let (Option::Some(l_bus), Option::Some(r_bus)) = (&left.bus, &right.bus) { + equal = l_bus.eq(r_bus); + } if let (Option::Some(l_dim), Option::Some(r_dim)) = (&left.arithmetic, &right.arithmetic) { equal = *l_dim == *r_dim; } @@ -88,18 +104,26 @@ pub fn type_check(program_archive: &ProgramArchive) -> Result bool { + let bus_data = program_archive.get_bus_data(&bus_name); + let mut tag_in_inputs = false; + for (_name, info) in bus_data.get_fields() { + if !info.get_tags().is_empty(){ + tag_in_inputs = true; + break; + } else if let WireType::Bus(bus_name) = info.get_type() { + if check_bus_contains_tag_recursive(bus_name, program_archive){ + tag_in_inputs = true; + break; + } + } + } + tag_in_inputs +} + fn type_statement( statement: &Statement, program_archive: &ProgramArchive, @@ -142,6 +183,13 @@ fn type_statement( &mut analysis_information.reports, ); } + else if dim_type.is_bus() { + add_report( + ReportCode::InvalidArraySizeB, + dim_expression.get_meta(), + &mut analysis_information.reports, + ); + } else if dim_type.dim() > 0 { add_report( ReportCode::InvalidArraySize(dim_type.dim()), @@ -152,23 +200,40 @@ fn type_statement( } match xtype { VariableType::Signal(s_type, tags) => { - if let SignalType::Input = s_type { - analysis_information.environment.add_input(name, (dimensions.len(),tags.clone())); - } else if let SignalType::Output = s_type { - analysis_information.environment.add_output(name, (dimensions.len(),tags.clone())); - } else { - analysis_information.environment.add_intermediate(name, (dimensions.len(),tags.clone())); + match s_type { + SignalType::Input => analysis_information + .environment + .add_input(name, (dimensions.len(),tags.clone())), + SignalType::Output => analysis_information + .environment + .add_output(name, (dimensions.len(),tags.clone())), + SignalType::Intermediate => analysis_information + .environment + .add_intermediate(name, (dimensions.len(),tags.clone())), } } - VariableType::Var => { - analysis_information.environment.add_variable(name, dimensions.len()) - } + VariableType::Var => analysis_information + .environment + .add_variable(name, dimensions.len()), VariableType::Component => analysis_information .environment .add_component(name, (meta.component_inference.clone(), dimensions.len())), VariableType::AnonymousComponent => analysis_information .environment .add_component(name, (meta.component_inference.clone(), dimensions.len())), + VariableType::Bus(tname, ss_type, tags) => { + match ss_type { + SignalType::Input => analysis_information + .environment + .add_input_bus(name, (Option::Some(tname.clone()), dimensions.len(), tags.clone())), + SignalType::Output => analysis_information + .environment + .add_output_bus(name, (Option::Some(tname.clone()), dimensions.len(), tags.clone())), + SignalType::Intermediate => analysis_information + .environment + .add_intermediate_bus(name, (Option::Some(tname.clone()), dimensions.len(), tags.clone())), + } + } } } Substitution { var, access, op, rhe, meta, .. } => { @@ -180,7 +245,7 @@ fn type_statement( }; let access_information_result = - treat_access(access, meta, program_archive, analysis_information); + treat_access(access, program_archive, analysis_information); let access_information = if let Result::Ok(info) = access_information_result { info @@ -188,18 +253,10 @@ fn type_statement( return; }; - if analysis_information.environment.has_component(var) && access_information.2.is_some(){ - return add_report( - ReportCode::OutputTagCannotBeModifiedOutside, - meta, - &mut analysis_information.reports, - ); - } - let symbol_type_result = apply_access_to_symbol( var, meta, - access_information, + access_information.clone(), &analysis_information.environment, &mut analysis_information.reports, program_archive, @@ -214,9 +271,29 @@ fn type_statement( (SymbolInformation::Signal(_), AssignOp::AssignConstraintSignal) | (SymbolInformation::Signal(_), AssignOp::AssignSignal) | (SymbolInformation::Var(_), AssignOp::AssignVar) - | (SymbolInformation::Component(_), AssignOp::AssignVar) => {} - | (SymbolInformation::Tag, AssignOp::AssignVar) => {} - (SymbolInformation::Signal(_), AssignOp::AssignVar)=>{ + | (SymbolInformation::Component(_), AssignOp::AssignVar) + | (SymbolInformation::Bus(_,_), AssignOp::AssignConstraintSignal) + | (SymbolInformation::Bus(_,_), AssignOp::AssignSignal) => {} + | (SymbolInformation::Bus(_,_), AssignOp::AssignVar) => { + if !rhe.is_bus_call() && !rhe.is_bus_call_array(){ + return add_report(ReportCode::WrongTypesInAssignOperationBus, meta, &mut analysis_information.reports); + } + } + | (SymbolInformation::Tag, AssignOp::AssignVar) => { + //If the tag comes from an output wire, it cannot be modified. + if analysis_information.environment.has_component(var){ + let (comp,_) = analysis_information.environment.get_component(var).unwrap(); + if comp.is_some() && access_information.1.is_some() && access_information.1.as_ref().unwrap().len() > 0 { + let template_name = comp.clone().unwrap(); + let (first_access,_) = access_information.1.as_ref().unwrap().get(0).unwrap(); + let output = program_archive.get_template_data(&template_name).get_output_info(&first_access); + if output.is_some() { + return add_report( ReportCode::OutputTagCannotBeModifiedOutside,meta, &mut analysis_information.reports); + } + } + } + } + (SymbolInformation::Signal(_), AssignOp::AssignVar) => { return add_report( ReportCode::WrongTypesInAssignOperationOperatorSignal, meta, @@ -232,14 +309,14 @@ fn type_statement( } } match symbol_information { - SymbolInformation::Component(possible_template) =>{ - if rhe_type.is_template(){ - if possible_template.is_none(){ + SymbolInformation::Component(possible_template) => { + if rhe_type.is_template() { + if possible_template.is_none() { let (current_template, _) = analysis_information .environment .get_mut_component_or_break(var, file!(), line!()); *current_template = rhe_type.template; - } else{ + } else { let template = possible_template.unwrap(); let r_template = rhe_type.template.unwrap(); if template != r_template { @@ -250,7 +327,7 @@ fn type_statement( ) } } - } else{ + } else { add_report( ReportCode::WrongTypesInAssignOperationTemplate, meta, @@ -258,53 +335,90 @@ fn type_statement( ) } } - SymbolInformation::Signal(dim) =>{ - if rhe_type.is_template(){ + SymbolInformation::Bus(possible_bus, dim) => { + if rhe_type.is_bus() { + if dim != rhe_type.dim() { + add_report( + ReportCode::WrongTypesInAssignOperationDims(dim, rhe_type.dim()), + meta, + &mut analysis_information.reports, + ) + } + else if possible_bus.is_none() { + let (current_bus, _,_) = analysis_information + .environment + .get_mut_bus_or_break(var, file!(), line!()); + *current_bus = rhe_type.bus; + } else { + let bus = possible_bus.unwrap(); + let r_bus = rhe_type.bus.unwrap(); + if bus != r_bus { + if dim > 0 { + add_report( + ReportCode::WrongTypesInAssignOperationArrayBuses, + meta, + &mut analysis_information.reports, + ) + } + else { + add_report( + ReportCode::WrongTypesInAssignOperationBus, + meta, + &mut analysis_information.reports, + ) + } + } + } + } else { add_report( - ReportCode::WrongTypesInAssignOperationExpression, + ReportCode::WrongTypesInAssignOperationBus, meta, &mut analysis_information.reports, ) - } else if dim != rhe_type.dim(){ + } + } + SymbolInformation::Signal(dim) + | SymbolInformation::Var(dim) => { + if rhe_type.is_template() { add_report( - ReportCode::WrongTypesInAssignOperationDims(dim, rhe_type.dim()), + ReportCode::WrongTypesInAssignOperationExpression, meta, &mut analysis_information.reports, ) - } - - } - SymbolInformation::Var(dim) =>{ - if rhe_type.is_template(){ + } else if rhe_type.is_bus() { add_report( - ReportCode::WrongTypesInAssignOperationExpression, + ReportCode::WrongTypesInAssignOperationBus, meta, &mut analysis_information.reports, ) - } else if dim != rhe_type.dim(){ + } else if dim != rhe_type.dim() { add_report( - ReportCode::WrongTypesInAssignOperationDims(dim, rhe_type.dim()), + ReportCode::WrongTypesInAssignOperationDims(dim, rhe_type.dim()), meta, &mut analysis_information.reports, ) } - } - SymbolInformation::Tag =>{ - if rhe_type.is_template(){ + SymbolInformation::Tag => { + if rhe_type.is_template() { add_report( ReportCode::WrongTypesInAssignOperationExpression, meta, &mut analysis_information.reports, ) - } else if 0 != rhe_type.dim(){ + } else if rhe_type.is_bus() { + add_report( + ReportCode::WrongTypesInAssignOperationBus, + meta, + &mut analysis_information.reports, + ) + } else if 0 != rhe_type.dim() { add_report( ReportCode::WrongTypesInAssignOperationDims(0, rhe_type.dim()), meta, &mut analysis_information.reports, ) } - } } } @@ -335,7 +449,26 @@ fn type_statement( &mut analysis_information.reports, ); } - if rhe_type.dim() != lhe_type.dim() { + if lhe_type.is_bus() || rhe_type.is_bus() { + match (lhe_type.bus, rhe_type.bus) { + (Some(b1),Some(b2)) => { + if b1 != b2 { + add_report(ReportCode::MustBeSameBus, lhe.get_meta(), + &mut analysis_information.reports); + } + } + (Some(_),_)=>{ + add_report(ReportCode::MustBeBus, rhe.get_meta(), + &mut analysis_information.reports); + }, + (_,Some(_)) => { + add_report(ReportCode::MustBeBus, lhe.get_meta(), + &mut analysis_information.reports); + }, + (_,_) => {unreachable!("At least one of them is a bus.")} + } + } + else if rhe_type.dim() != lhe_type.dim() { add_report( ReportCode::MustBeSameDimension(rhe_type.dim(), lhe_type.dim()), rhe.get_meta(), @@ -352,12 +485,18 @@ fn type_statement( } else { return; }; - if arg_type.is_template() { + if arg_type.is_template() { add_report( ReportCode::MustBeSingleArithmeticT, meta, &mut analysis_information.reports, ) + } else if arg_type.is_bus() { + add_report( + ReportCode::MustBeSingleArithmeticB, + meta, + &mut analysis_information.reports, + ) } else if arg_type.dim() > 0 { add_report( ReportCode::MustBeSingleArithmetic(arg_type.dim()), @@ -381,7 +520,13 @@ fn type_statement( meta, &mut analysis_information.reports, ) - } else if arg_type.dim() > 0 { + } else if arg_type.is_bus() { + add_report( + ReportCode::MustBeSingleArithmeticB, + meta, + &mut analysis_information.reports, + ) + } else if arg_type.dim() > 0 { add_report( ReportCode::MustBeSingleArithmetic(arg_type.dim()), meta, @@ -418,13 +563,19 @@ fn type_statement( } else { return; }; - if cond_type.is_template(){ + if cond_type.is_template() { add_report( ReportCode::MustBeSingleArithmeticT, cond.get_meta(), &mut analysis_information.reports, ) - }else if cond_type.dim() > 0 { + } else if cond_type.is_bus() { + add_report( + ReportCode::MustBeSingleArithmeticB, + cond.get_meta(), + &mut analysis_information.reports, + ) + } else if cond_type.dim() > 0 { add_report( ReportCode::MustBeSingleArithmetic(cond_type.dim()), cond.get_meta(), @@ -440,13 +591,19 @@ fn type_statement( } else { return; }; - if cond_type.is_template(){ + if cond_type.is_template() { add_report( ReportCode::MustBeSingleArithmeticT, cond.get_meta(), &mut analysis_information.reports, ) - }else if cond_type.dim() > 0 { + } else if cond_type.is_bus() { + add_report( + ReportCode::MustBeSingleArithmeticB, + cond.get_meta(), + &mut analysis_information.reports, + ) + } else if cond_type.dim() > 0 { add_report( ReportCode::MustBeSingleArithmetic(cond_type.dim()), cond.get_meta(), @@ -479,6 +636,7 @@ fn type_statement( }, } } + fn type_expression( expression: &Expression, program_archive: &ProgramArchive, @@ -505,6 +663,12 @@ fn type_expression( expression.get_meta(), &mut analysis_information.reports, ); + } else if value_type.is_bus() { + add_report( + ReportCode::InvalidArrayTypeB, + expression.get_meta(), + &mut analysis_information.reports, + ); } else if inferred_dim != value_type.dim() { add_report( ReportCode::NonHomogeneousArray(inferred_dim, value_type.dim()), @@ -531,15 +695,24 @@ fn type_expression( expression.get_meta(), &mut analysis_information.reports, ); - } else if dim_type.dim() != 0 { + } else if dim_type.is_bus() { + add_report( + ReportCode::InvalidArrayTypeB, + expression.get_meta(), + &mut analysis_information.reports, + ); + }else if dim_type.dim() != 0 { add_report( ReportCode::InvalidArrayType, expression.get_meta(), &mut analysis_information.reports, ); } - - Result::Ok(FoldedType::arithmetic_type(value_type.dim() + 1)) + if let Some(iden) = &value_type.bus { + Result::Ok(FoldedType::bus(iden.clone().as_str(), value_type.dim() + 1)) + } else { + Result::Ok(FoldedType::arithmetic_type(value_type.dim() + 1)) + } } InfixOp { lhe, rhe, .. } => { let lhe_response = type_expression(lhe, program_archive, analysis_information); @@ -547,14 +720,14 @@ fn type_expression( let lhe_type = lhe_response?; let rhe_type = rhe_response?; let mut successful = Result::Ok(()); - if lhe_type.is_template() || lhe_type.dim() > 0 { + if lhe_type.is_template() || lhe_type.is_bus() || lhe_type.dim() > 0 { successful = add_report_and_end( ReportCode::InfixOperatorWithWrongTypes, lhe.get_meta(), &mut analysis_information.reports, ); } - if rhe_type.is_template() || rhe_type.dim() > 0 { + if rhe_type.is_template() || rhe_type.is_bus() || rhe_type.dim() > 0 { successful = add_report_and_end( ReportCode::InfixOperatorWithWrongTypes, rhe.get_meta(), @@ -566,7 +739,7 @@ fn type_expression( } PrefixOp { rhe, .. } => { let rhe_type = type_expression(rhe, program_archive, analysis_information)?; - if rhe_type.is_template() || rhe_type.dim() > 0 { + if rhe_type.is_template() || rhe_type.is_bus() || rhe_type.dim() > 0 { add_report_and_end( ReportCode::PrefixOperatorWithWrongTypes, rhe.get_meta(), @@ -576,9 +749,9 @@ fn type_expression( Result::Ok(FoldedType::arithmetic_type(0)) } } - ParallelOp {rhe, .. } =>{ + ParallelOp {rhe, .. } => { let rhe_type = type_expression(rhe, program_archive, analysis_information)?; - if rhe_type.is_template() { + if rhe_type.is_template() { Result::Ok(rhe_type) } else { add_report_and_end( @@ -591,8 +764,7 @@ fn type_expression( InlineSwitchOp { cond, if_true, if_false, .. } => { let cond_response = type_expression(cond, program_archive, analysis_information); let if_true_response = type_expression(if_true, program_archive, analysis_information); - let if_false_response = - type_expression(if_false, program_archive, analysis_information); + let if_false_response = type_expression(if_false, program_archive, analysis_information); let if_true_type = if_true_response?; let cond_type = if let Result::Ok(f) = cond_response { @@ -600,12 +772,18 @@ fn type_expression( } else { return Result::Ok(if_true_type); }; - if cond_type.is_template(){ + if cond_type.is_template() { add_report( ReportCode::MustBeSingleArithmeticT, cond.get_meta(), &mut analysis_information.reports, ) + } else if cond_type.is_bus() { + add_report( + ReportCode::MustBeSingleArithmeticB, + cond.get_meta(), + &mut analysis_information.reports, + ) } else if cond_type.dim() > 0 { add_report( @@ -632,7 +810,7 @@ fn type_expression( Variable { name, access, meta, .. } => { debug_assert!(analysis_information.environment.has_symbol(name)); let access_information = - treat_access( access, meta, program_archive, analysis_information)?; + treat_access( access, program_archive, analysis_information)?; let environment = &analysis_information.environment; let reports = &mut analysis_information.reports; let symbol_information = apply_access_to_symbol( @@ -647,15 +825,21 @@ fn type_expression( SymbolInformation::Component(possible_template) if possible_template.is_some() => { Result::Ok(FoldedType::template(&possible_template.unwrap())) } + SymbolInformation::Component(possible_template) if possible_template.is_none() => { + add_report_and_end(ReportCode::UninitializedSymbolInExpression, meta, reports) + } + SymbolInformation::Bus(possible_bus, dim) if possible_bus.is_some() => { + Result::Ok(FoldedType::bus(&possible_bus.unwrap(), dim)) + } + SymbolInformation::Bus(possible_bus, _) if possible_bus.is_none() => { + add_report_and_end(ReportCode::UninitializedSymbolInExpression, meta, reports) + } SymbolInformation::Var(dim) | SymbolInformation::Signal(dim) => { Result::Ok(FoldedType::arithmetic_type(dim)) } SymbolInformation::Tag => { Result::Ok(FoldedType::arithmetic_type(0)) } - SymbolInformation::Component(possible_template) if possible_template.is_none() => { - add_report_and_end(ReportCode::UninitializedSymbolInExpression, meta, reports) - } _ => unreachable!(), } } @@ -714,10 +898,64 @@ fn type_expression( analysis_information.file_id = previous_file_id; let folded_value = returned_type?; Result::Ok(folded_value) + }, + BusCall { meta, id, args } => { + analysis_information.reached.insert(id.clone()); + let typing_response = + type_array_of_expressions(args, program_archive, analysis_information); + if program_archive.contains_bus(id) && typing_response.is_err() { + return Result::Ok(FoldedType::bus(id,program_archive.get_bus_data(id).get_fields().len())); + } + let arg_types = typing_response?; + let mut concrete_types = Vec::new(); + let mut success = Result::Ok(()); + for (arg_expr, arg_type) in args.iter().zip(arg_types.iter()) { + if arg_type.is_template() { + success = add_report_and_end( + ReportCode::InvalidArgumentInBusInstantiationT, + arg_expr.get_meta(), + &mut analysis_information.reports, + ); + } else if arg_type.is_bus() { + success = add_report_and_end( + ReportCode::InvalidArgumentInBusInstantiationB, + arg_expr.get_meta(), + &mut analysis_information.reports, + ); + } + concrete_types.push(arg_type.dim()); + } + if program_archive.contains_bus(id) && success.is_err() { + return Result::Ok(FoldedType::bus(id,program_archive.get_bus_data(id).get_fields().len())); + } + success?; + let previous_file_id = analysis_information.file_id; + analysis_information.file_id = program_archive.get_bus_data(id).get_file_id(); + + let new_environment = prepare_environment_for_call( + meta, + id, + &concrete_types, + program_archive, + &mut analysis_information.reports, + ); + if new_environment.is_err() { + return Result::Ok(FoldedType::bus(id,program_archive.get_bus_data(id).get_fields().len())); + } + let new_environment = new_environment?; + let previous_environment = + std::mem::replace(&mut analysis_information.environment, new_environment); + let returned_type = type_bus(id, &concrete_types, analysis_information, program_archive); + analysis_information.environment = previous_environment; + analysis_information.file_id = previous_file_id; + let folded_value = returned_type?; + Result::Ok(folded_value) + } _ => {unreachable!("Anonymous calls should not be reachable at this point."); } } } + //************************************************* Statement support ************************************************* fn treat_sequence_of_statements( stmts: &[Statement], @@ -731,43 +969,45 @@ fn treat_sequence_of_statements( //************************************************* Expression support ************************************************* // 0: symbol dimensions accessed -// 1: Signal accessed and dimensions accessed in that signal (optional) -// 2: Tag accessed (optional) -type AccessInfo = (ArithmeticType, Option<(String, ArithmeticType)>, Option); +// 1: Vector of (Wire accessed and dimensions accessed in that wire) (optional) +// Size of this vector is equals to the number of buses (+1 if it finishes in a signal)) +// (+1 if it finishes in a tag) +type AccessInfo = (ArithmeticType, Option>); fn treat_access( accesses: &[Access], - meta: &Meta, program_archive: &ProgramArchive, analysis_information: &mut AnalysisInformation, ) -> Result { use Access::*; - let mut access_info: AccessInfo = (0, Option::None, Option::None); + let mut access_info: AccessInfo = (0, Option::None); + let mut signal_info : Vec<(String, ArithmeticType)> = Vec::new(); for access in accesses { match access { ArrayAccess(index) => { let index_response = type_expression(&index, program_archive, analysis_information); - - if access_info.2.is_some(){ - add_report( - ReportCode::InvalidArrayAccess(0, 1), - index.get_meta(), - &mut analysis_information.reports, - ); - } else{ - if let Option::Some(signal_info) = &mut access_info.1 { - signal_info.1 += 1; - } else { - access_info.0 += 1; - } - if let Result::Ok(index_type) = index_response { - if index_type.is_template() { - add_report( + if signal_info.len() > 0 { + let mut info = signal_info.get(signal_info.len()-1).unwrap().clone(); + info.1 = info.1 + 1; + signal_info.remove(signal_info.len()-1); + signal_info.push(info); + } else { + access_info.0 += 1; + } + if let Result::Ok(index_type) = index_response { + if index_type.is_template() { + add_report( ReportCode::InvalidArraySizeT, index.get_meta(), &mut analysis_information.reports, ); - } - else if index_type.dim() > 0 { + } else if index_type.is_bus() { + add_report( + ReportCode::InvalidArraySizeB, + index.get_meta(), + &mut analysis_information.reports, + ); + } + else if index_type.dim() > 0 { add_report( ReportCode::InvalidArraySize(index_type.dim()), index.get_meta(), @@ -775,35 +1015,31 @@ fn treat_access( ); } } - } } ComponentAccess(name) => { - if let Option::Some(_signal_info) = & access_info.1 { - if access_info.2.is_none(){ - access_info.2 = Some(name.clone()) - } else{ - add_report( - ReportCode::InvalidSignalTagAccess, - meta, - &mut analysis_information.reports, - ); - } + + if signal_info.len() > 0 { + signal_info.push((name.clone(),0)); } else { - access_info.1 = Option::Some((name.clone(), 0)); + signal_info = vec![(name.clone(), 0)]; } } } } + if signal_info.len() > 0{ + access_info.1 = Some(signal_info); + } else { access_info.1 = None; } Result::Ok(access_info) } - enum SymbolInformation { Component(Option), Var(ArithmeticType), Signal(ArithmeticType), + Bus(Option, ArithmeticType), Tag, } + fn apply_access_to_symbol( symbol: &str, meta: &Meta, @@ -812,12 +1048,14 @@ fn apply_access_to_symbol( reports: &mut ReportCollection, program_archive: &ProgramArchive, ) -> Result { - let (current_template, mut current_dim, possible_tags) = if environment.has_component(symbol) { + let (current_template_or_bus, mut current_dim, possible_tags) = if environment.has_component(symbol) { let (temp, dim) = environment.get_component_or_break(symbol, file!(), line!()).clone(); - (temp,dim, Vec::new()) + (temp.clone(),dim, Vec::new()) } else if environment.has_signal(symbol) { let(dim, tags) = environment.get_signal_or_break(symbol, file!(), line!()); - (Option::None, *dim, tags.clone()) + (Some(symbol.to_string()), *dim, tags.clone()) + } else if environment.has_bus(symbol){ + environment.get_bus_or_break(symbol, file!(), line!()).clone() } else { let dim = environment.get_variable_or_break(symbol, file!(), line!()); (Option::None, *dim, Vec::new()) @@ -828,69 +1066,138 @@ fn apply_access_to_symbol( } else { current_dim -= access_information.0 } + // Case wires or tags + if let Option::Some(buses_and_signals) = access_information.1{ + assert!(buses_and_signals.len() > 0); + let mut pos = 0; + if current_dim > 0 && (pos < buses_and_signals.len()- 1 || !possible_tags.contains(&buses_and_signals.get(0).unwrap().0)){ + return add_report_and_end(ReportCode::InvalidArrayAccess(current_dim+access_information.0,access_information.0), meta, reports); + } + if current_template_or_bus.is_none() && environment.has_bus(symbol){ + return add_report_and_end(ReportCode::InvalidTagAccess, meta, reports); + } else if current_template_or_bus.is_none() && environment.has_component(symbol){ + return add_report_and_end(ReportCode::UninitializedComponent, meta, reports); + } - // Case signals or tags - if let Option::Some((signal_name, dims_accessed)) = access_information.1{ - if current_template.is_some(){ // we are inside component + let (mut kind, mut tags) = if environment.has_component(symbol){ + // we are inside component if current_dim != 0{ // only allowed complete accesses to component return add_report_and_end(ReportCode::InvalidPartialArray, meta, reports); } - - let template_name = current_template.unwrap(); - let input = program_archive.get_template_data(&template_name).get_input_info(&signal_name); - let output = program_archive.get_template_data(&template_name).get_output_info(&signal_name); - let tags; - (current_dim, tags) = match (input, output) { - (Option::Some((d, tags)), _) | (_, Option::Some((d, tags))) => (*d, tags), + //current_dim == 0 => component completely defined + let template_name = current_template_or_bus.unwrap(); + let (accessed_element,accessed_dim) = buses_and_signals.get(pos).unwrap(); + let input = program_archive.get_template_data(&template_name).get_input_info(&accessed_element); + let output = program_archive.get_template_data(&template_name).get_output_info(&accessed_element); + let (dim, kind, atags) = match (input, output) { + (Option::Some(wire_data), _) | (_, Option::Some(wire_data)) => + (wire_data.get_dimension(), wire_data.get_type(), wire_data.get_tags()), _ => { return add_report_and_end(ReportCode::InvalidSignalAccess, meta, reports); } }; - if access_information.2.is_some(){ // tag of io signal of component - if dims_accessed > 0{ - return add_report_and_end(ReportCode::InvalidTagAccessAfterArray, meta, reports); - } - else if !tags.contains(&access_information.2.unwrap()){ - return add_report_and_end(ReportCode::InvalidTagAccess, meta, reports); - } else{ - return Result::Ok(SymbolInformation::Tag); + + if *accessed_dim > dim { + return add_report_and_end(ReportCode::InvalidArrayAccess(dim, *accessed_dim), meta, reports); + } + current_dim = dim - accessed_dim; + if pos == buses_and_signals.len()-1 { + match kind { + WireType::Signal => {return Result::Ok(SymbolInformation::Signal(current_dim));}, + WireType::Bus(b_name) => {return Result::Ok(SymbolInformation::Bus(Some(b_name.clone()),current_dim))}, } - } else{ // io signal of component - if dims_accessed > current_dim { - return add_report_and_end(ReportCode::InvalidArrayAccess(current_dim, dims_accessed), meta, reports); - } else { - return Result::Ok(SymbolInformation::Signal(current_dim - dims_accessed)); - } } - } else{ // we are in template - if environment.has_signal(symbol){ - if access_information.0 != 0{ - add_report_and_end(ReportCode::InvalidTagAccessAfterArray, meta, reports) - } else if dims_accessed > 0{ - add_report_and_end( - ReportCode::InvalidArrayAccess(0, dims_accessed), - meta, - reports, - ) - } else if !possible_tags.contains(&signal_name){ - add_report_and_end(ReportCode::InvalidTagAccess, meta, reports) - } else{ - Result::Ok(SymbolInformation::Tag) + pos += 1; + (kind, atags.clone()) + } else if environment.has_bus(symbol){ + let kind = WireType::Bus(current_template_or_bus.unwrap()); + let mut tags = HashSet::new(); + for i in possible_tags.clone() { + tags.insert(i); + } + (kind, tags) + } else { + let kind = WireType::Signal; + let mut tags = HashSet::new(); + for i in possible_tags.clone() { + tags.insert(i); + } + (kind,tags) + }; + while pos < buses_and_signals.len() { + let (accessed_element, accessed_dim) = buses_and_signals.get(pos).unwrap().clone(); + if current_dim > 0 && (pos < buses_and_signals.len()- 1 || !tags.contains(&accessed_element)){ + return add_report_and_end(ReportCode::InvalidArrayAccess(current_dim,accessed_dim), meta, reports); + } + if kind == WireType::Signal { + if tags.contains(&accessed_element) { + let prev_dim_access = if buses_and_signals.len()>1 {buses_and_signals.get(buses_and_signals.len()-2).unwrap().1} + else {access_information.0}; + //Tags cannot be partially accessed. Then, the previous bus or signal cannot be array accessed. + if pos == buses_and_signals.len()-1 && 0 == prev_dim_access && accessed_dim == 0{ + return Result::Ok(SymbolInformation::Tag); + } else if prev_dim_access > 0 { + return add_report_and_end(ReportCode::InvalidTagAccessAfterArray, meta, reports); + } else{ + return add_report_and_end(ReportCode::InvalidTagAccess, meta, reports); + } + } + else{ + return add_report_and_end(ReportCode::InvalidTagAccess, meta, reports); + } + } else if let WireType::Bus(b_name) = kind { + let field = program_archive.get_bus_data(&b_name).get_field_info(&accessed_element); + match field { + Some(wire) => { + if accessed_dim > wire.get_dimension() { + return add_report_and_end(ReportCode::InvalidArrayAccess(wire.get_dimension(), accessed_dim), meta, reports); + } + current_dim = wire.get_dimension() - accessed_dim; + if pos == buses_and_signals.len()-1 { + match wire.get_type() { + WireType::Signal => {return Result::Ok(SymbolInformation::Signal(current_dim));}, + WireType::Bus(b_name2) => {return Result::Ok(SymbolInformation::Bus(Some(b_name2.clone()), current_dim))}, + } + } else { + kind = wire.get_type(); + tags = wire.get_tags().clone(); + } + }, + Option::None => { + if tags.contains(&accessed_element) { + let prev_dim_access = if buses_and_signals.len()>1 {buses_and_signals.get(buses_and_signals.len()-2).unwrap().1} + else {access_information.0}; + if pos == buses_and_signals.len()-1 && prev_dim_access == 0 && accessed_dim == 0{ + return Result::Ok(SymbolInformation::Tag); + } else if prev_dim_access > 0 { + return add_report_and_end(ReportCode::InvalidTagAccessAfterArray, meta, reports); + } else{ + return add_report_and_end(ReportCode::InvalidTagAccess, meta, reports); + } + } + else{ + return add_report_and_end(ReportCode::InvalidTagAccess, meta, reports); + } + }, } - - } else if environment.has_component(symbol){ - add_report_and_end(ReportCode::UninitializedComponent, meta, reports) } else{ - add_report_and_end(ReportCode::InvalidSignalTagAccess, meta, reports) + unreachable!() } + pos += 1; } + unreachable!() + + + //add_report_and_end(ReportCode::InvalidTagAccessAfterArray, meta, reports); } else if environment.has_variable(symbol) { Result::Ok(SymbolInformation::Var(current_dim)) } else if environment.has_signal(symbol) { Result::Ok(SymbolInformation::Signal(current_dim)) - } else if environment.has_component(symbol) && current_dim == 0 { - Result::Ok(SymbolInformation::Component(current_template)) + } else if environment.has_bus(symbol){ + Result::Ok(SymbolInformation::Bus(current_template_or_bus, current_dim)) + }else if environment.has_component(symbol) && current_dim == 0 { + Result::Ok(SymbolInformation::Component(current_template_or_bus)) } else { add_report_and_end(ReportCode::InvalidPartialArray, meta, reports) } @@ -928,8 +1235,10 @@ fn prepare_environment_for_call( ) -> Result { let args_names = if program_archive.contains_function(call_id) { program_archive.get_function_data(call_id).get_name_of_params() - } else { + } else if program_archive.contains_template(call_id) { program_archive.get_template_data(call_id).get_name_of_params() + } else { + program_archive.get_bus_data(call_id).get_name_of_params() }; if args_dims.len() != args_names.len() { let error_code = ReportCode::WrongNumberOfArguments(args_names.len(), args_dims.len()); @@ -957,6 +1266,17 @@ fn type_template( call_id.to_string() } + +fn type_bus(id: &str, args_dims: &[ArithmeticType], analysis_information: &mut AnalysisInformation, program_archive: &ProgramArchive) -> Result { + debug_assert!(program_archive.contains_bus(id)); + if analysis_information.registered_calls.get_instance(id, args_dims).is_none() { + analysis_information.registered_calls.add_instance(id, args_dims.to_vec(), 0); + let stmts = program_archive.get_bus_data(id).get_body_as_vec(); + treat_sequence_of_statements(stmts, program_archive, analysis_information); + } + Result::Ok(FoldedType::bus(id,0)) +} + fn type_function( call_id: &str, args_dims: &[ArithmeticType], @@ -1018,6 +1338,9 @@ fn add_report(error_code: ReportCode, meta: &Meta, reports: &mut ReportCollectio InvalidArraySizeT =>{ "Array indexes and lengths must be single arithmetic expressions.\n Found component instead of expression.".to_string() } + InvalidArraySizeB =>{ + "Array indexes and lengths must be single arithmetic expressions.\n Found bus instead of expression.".to_string() + } InvalidArrayAccess(expected, given) => { format!("Array access does not match the dimensions of the expression. \n Expected {} dimensions, given {}.", expected, given @@ -1028,6 +1351,7 @@ fn add_report(error_code: ReportCode, meta: &Meta, reports: &mut ReportCollectio InvalidTagAccess => "Tag not found in signal: only accesses to tags that appear in the definition of the signal are allowed".to_string(), InvalidTagAccessAfterArray => "Invalid access to the tag of an array element: tags belong to complete arrays, not to individual positions.\n Hint: instead of signal[pos].tag use signal.tag".to_string(), InvalidArrayType => "Components can not be declared inside inline arrays".to_string(), + InvalidArrayTypeB => "Buses can not be declared inside inline arrays".to_string(), InfixOperatorWithWrongTypes | PrefixOperatorWithWrongTypes => { "Type not allowed by the operator".to_string() } @@ -1043,13 +1367,17 @@ fn add_report(error_code: ReportCode, meta: &Meta, reports: &mut ReportCollectio format!("The operator does not match the types of the assigned elements.\n Only assignments to signals allow the operators <== and <--, try using = instead") } WrongTypesInAssignOperationArrayTemplates => "Assignee and assigned types do not match.\n All components of an array must be instances of the same template.".to_string(), - WrongTypesInAssignOperationTemplate => "Assignee and assigned types do not match.\n Expected template found expression.".to_string(), + WrongTypesInAssignOperationTemplate => "Assignee and assigned types do not match.\n Expected template but found expression.".to_string(), + WrongTypesInAssignOperationArrayBuses => "Assignee and assigned types do not match.\n All buses of an array must be the same type.".to_string(), + WrongTypesInAssignOperationBus => "Assignee and assigned types do not match.\n Expected bus but found a different expression.".to_string(), WrongTypesInAssignOperationExpression => "Assignee and assigned types do not match.\n Expected expression found template.".to_string(), WrongTypesInAssignOperationDims(expected, found) => { format!("Assignee and assigned types do not match. \n Expected dimensions: {}, found {}", expected, found) } InvalidArgumentInCall => "Components can not be passed as arguments".to_string(), + InvalidArgumentInBusInstantiationT => "Components can not be passed as arguments".to_string(), + InvalidArgumentInBusInstantiationB => "Buses can not be passed as arguments".to_string(), UnableToTypeFunction => "Unable to infer the type of this function".to_string(), MustBeSingleArithmetic(dim) => { format!("Must be a single arithmetic expression.\n Found expression of {} dimensions", dim) @@ -1057,6 +1385,7 @@ fn add_report(error_code: ReportCode, meta: &Meta, reports: &mut ReportCollectio MustBeSingleArithmeticT => { format!("Must be a single arithmetic expression.\n Found component") } + MustBeSingleArithmeticB => format!("Must be a single arithmetic expression.\n Found bus"), MustBeArithmetic => "Must be a single arithmetic expression or an array of arithmetic expressions. \n Found component".to_string(), OutputTagCannotBeModifiedOutside => "Output tag from a subcomponent cannot be modified".to_string(), MustBeSameDimension(dim_1, dim_2) =>{ @@ -1071,6 +1400,9 @@ fn add_report(error_code: ReportCode, meta: &Meta, reports: &mut ReportCollectio } UninitializedComponent => "Trying to access to a signal of a component that has not been initialized".to_string(), NonCompatibleBranchTypes => "Inline switch operator branches types are non compatible".to_string(), + MustBeSameBus => "Both kind of buses must be equals".to_string(), + MustBeBus => "Expected to be a bus".to_string(), + InvalidSignalAccessInBus => format!("Field not defined in bus"), IllegalMainExpression => "Invalid main component: the main component should be a template, not a function call or expression".to_string(), e => panic!("Unimplemented error code: {}", e), }; diff --git a/type_analysis/src/analyzers/unknown_known_analysis.rs b/type_analysis/src/analyzers/unknown_known_analysis.rs index bf93e8a22..61487d809 100644 --- a/type_analysis/src/analyzers/unknown_known_analysis.rs +++ b/type_analysis/src/analyzers/unknown_known_analysis.rs @@ -36,24 +36,38 @@ enum Tag { // if (a[i] == 5){ in === 5;} // we do not know if there is an error here or not // --> we cannot detect the error until execution -type Environment = CircomEnvironment; +type Environment = CircomEnvironment; pub fn unknown_known_analysis( - template_name: &str, + name: &str, program_archive: &ProgramArchive, ) -> Result<(), ReportCollection> { debug_assert!(Tag::Known < Tag::Unknown); - let template_data = program_archive.get_template_data(template_name); - let template_body = template_data.get_body(); - let file_id = template_data.get_file_id(); let mut environment = Environment::new(); - for arg in template_data.get_name_of_params() { - // We do not know if it is an array or not, so we use the most restrictive option - environment.add_variable(arg, (Tag::Known, true)); - } + let (body, file_id) = if program_archive.contains_template(name) { + let template_data = program_archive.get_template_data(name); + let template_body = template_data.get_body(); + let file_id = template_data.get_file_id(); + for arg in template_data.get_name_of_params() { + // We do not know if it is an array or not, so we use the most restrictive option + environment.add_variable(arg, (Tag::Known, true)); + } + (template_body, file_id) + } else { + debug_assert!(program_archive.contains_bus(name)); + let bus_data = program_archive.get_bus_data(name); + let bus_body = bus_data.get_body(); + let file_id = bus_data.get_file_id(); + for arg in bus_data.get_name_of_params() { + // We do not know if it is an array or not, so we use the most restrictive option + environment.add_variable(arg, (Tag::Known, true)); + } + (bus_body, file_id) + }; + let entry = EntryInformation { file_id, environment }; - let result = analyze(template_body, entry); + let result = analyze(body, entry); if result.reports.is_empty() { Result::Ok(()) } else { @@ -63,7 +77,6 @@ pub fn unknown_known_analysis( fn analyze(stmt: &Statement, entry_information: EntryInformation) -> ExitInformation { use Statement::*; - use Symbol::*; use Tag::*; fn iterate_statements( @@ -99,83 +112,120 @@ fn analyze(stmt: &Statement, entry_information: EntryInformation) -> ExitInforma let mut signals_declared = false; match stmt { Declaration { xtype, name, dimensions, .. } => { - if let VariableType::Signal(..) = xtype { - environment.add_intermediate(name, Unknown); - signals_declared = true; - } else if let VariableType::Component = xtype { - environment.add_component(name, Unknown); - signals_declared = true; - } else if let VariableType::AnonymousComponent = xtype { - environment.add_component(name, Unknown); - signals_declared = true; - } else { // it is a variable - let is_array = dimensions.len() > 0; - environment.add_variable(name, (Known, is_array)); - modified_variables.insert(name.clone()); + match xtype { + VariableType::Var => { + let is_array = dimensions.len() > 0; + environment.add_variable(name, (Known, is_array)); + modified_variables.insert(name.clone()); + } + VariableType::Signal(..) => { + environment.add_intermediate(name, Unknown); + signals_declared = true; + } + VariableType::Bus(..) => { + environment.add_intermediate_bus(name, Unknown); + signals_declared = true; + } + VariableType::Component + | VariableType::AnonymousComponent => { + environment.add_component(name, Unknown); + signals_declared = true; + } } - if let VariableType::AnonymousComponent = xtype { - // in this case the dimension is ukn - } else{ - for dimension in dimensions { - if tag(dimension, &environment) == Unknown { - add_report( - ReportCode::UnknownDimension, - dimension.get_meta(), - file_id, - &mut reports, - ); - } + + match xtype { + VariableType::AnonymousComponent => {} + _ => { + for dimension in dimensions { + if tag(dimension, &environment) == Unknown { + add_report( + ReportCode::UnknownDimension, + dimension.get_meta(), + file_id, + &mut reports, + ); + } + } } } - } Substitution { meta, var, access, op, rhe, .. } => { - let simplified_elem = simplify_symbol(&environment, var, access); + let reduced_type = meta.get_type_knowledge().get_reduces_to(); let expression_tag = tag(rhe, &environment); let mut access_tag = Known; for acc in access { match acc { - Access::ArrayAccess(exp) if access_tag != Unknown => { + Access::ArrayAccess(exp) => { access_tag = tag(exp, &environment); } _ => {} } - } - if simplified_elem == Variable { - let (value, is_array) = environment.get_mut_variable_or_break(var, file!(), line!()); - if !*is_array { // if it is a single variable we always update - *value = max(expression_tag, access_tag); - } else if *value == Known{ // if not, if it was ukn it remains ukn - *value = max(expression_tag, access_tag); + if access_tag == Unknown { + break; } - modified_variables.insert(var.clone()); - } else if simplified_elem == Component { - constraints_declared = true; - if expression_tag == Unknown { - add_report(ReportCode::UnknownTemplate, rhe.get_meta(), file_id, &mut reports); + } + match reduced_type { + TypeReduction::Variable => { + let (value, is_array) = environment.get_mut_variable_or_break(var, file!(), line!()); + if !*is_array { // if it is a single variable we always update + *value = max(expression_tag, access_tag); + } else if *value == Known{ // if not, if it was ukn it remains ukn + *value = max(expression_tag, access_tag); + } + modified_variables.insert(var.clone()); + } - if access_tag == Unknown { - add_report(ReportCode::UnknownTemplate, meta, file_id, &mut reports); + TypeReduction::Component(_) => { + constraints_declared = true; + if expression_tag == Unknown { + add_report(ReportCode::UnknownTemplate, rhe.get_meta(), file_id, &mut reports); + } + if access_tag == Unknown { + add_report(ReportCode::UnknownTemplate, meta, file_id, &mut reports); + } } - } else if simplified_elem == SignalTag { - tags_modified = true; - if expression_tag == Unknown { - add_report(ReportCode::NonValidTagAssignment, rhe.get_meta(), file_id, &mut reports); + TypeReduction::Bus(_) => { + if *op == AssignOp::AssignVar && expression_tag == Unknown { + add_report(ReportCode::UnknownBus, meta, file_id, &mut reports); + } + if *op == AssignOp::AssignConstraintSignal { + constraints_declared = true; + if is_non_quadratic(rhe, &environment) { + add_report(ReportCode::NonQuadratic, rhe.get_meta(), file_id, &mut reports); + } + if access_tag == Unknown { + add_report(ReportCode::NonQuadratic, meta, file_id, &mut reports); + } + } } - if access_tag == Unknown { - add_report(ReportCode::NonValidTagAssignment, rhe.get_meta(), file_id, &mut reports); - } - } else if *op == AssignOp::AssignConstraintSignal { - constraints_declared = true; - if is_non_quadratic(rhe, &environment) { - add_report(ReportCode::NonQuadratic, rhe.get_meta(), file_id, &mut reports); + TypeReduction::Tag => { + tags_modified = true; + if expression_tag == Unknown { + add_report(ReportCode::NonValidTagAssignment, rhe.get_meta(), file_id, &mut reports); + } + if access_tag == Unknown { + add_report(ReportCode::NonValidTagAssignment, meta, file_id, &mut reports); + } } - if access_tag == Unknown { - add_report(ReportCode::NonQuadratic, meta, file_id, &mut reports); + _ => { + if *op == AssignOp::AssignConstraintSignal { + constraints_declared = true; + if is_non_quadratic(rhe, &environment) { + add_report(ReportCode::NonQuadratic, rhe.get_meta(), file_id, &mut reports); + } + if access_tag == Unknown { + add_report(ReportCode::NonQuadratic, meta, file_id, &mut reports); + } + } + else if environment.has_component(var){ + if access_tag == Unknown { + add_report(ReportCode::UnknownTemplateAssignment, meta, file_id, &mut reports); + } + } } } } - UnderscoreSubstitution { op, rhe, .. } => { + UnderscoreSubstitution { op, rhe, .. } => { let _expression_tag = tag(rhe, &environment); if *op == AssignOp::AssignConstraintSignal { constraints_declared = true; @@ -183,7 +233,7 @@ fn analyze(stmt: &Statement, entry_information: EntryInformation) -> ExitInforma add_report(ReportCode::NonQuadratic, rhe.get_meta(), file_id, &mut reports); } } - }, + } ConstraintEquality { lhe, rhe, .. } => { constraints_declared = true; if is_non_quadratic(lhe, &environment) { @@ -196,7 +246,7 @@ fn analyze(stmt: &Statement, entry_information: EntryInformation) -> ExitInforma IfThenElse { cond, if_case, else_case, .. } => { let tag_cond = tag(cond, &environment); let new_entry_else_case = - EntryInformation { environment: environment.clone(), file_id}; + EntryInformation { environment: environment.clone(), file_id }; let new_entry_if_case = EntryInformation { environment, file_id }; let if_case_info = analyze(if_case, new_entry_if_case); let else_case_info = if let Option::Some(else_stmt) = else_case { @@ -349,40 +399,26 @@ fn tag(expression: &Expression, environment: &Environment) -> Tag { use Tag::*; match expression { Number(_, _) => Known, - Variable { name, access, .. } => { - let mut symbol_tag = if environment.has_variable(name) { - let (tag, is_array) = environment.get_variable_or_break(name, file!(), line!()); - if *is_array{ - Known - } else{ - *tag - } - } else if environment.has_component(name) { - *environment.get_component_or_break(name, file!(), line!()) - } else { - if environment.has_intermediate(name) && !all_array_are_accesses(access) { - Known /* In this case, it is a tag. */ - } else{ - *environment.get_intermediate_or_break(name, file!(), line!()) - } - }; - let mut index = 0; - loop { - if index == access.len() { - break symbol_tag; - } - if symbol_tag == Unknown { - break Unknown; - } - if let Access::ArrayAccess(exp) = &access[index] { - symbol_tag = tag(exp, environment); - } else if !environment.has_intermediate(name) { - symbol_tag = Unknown; - } - index += 1; + Variable { meta, name,.. } => { + let reduced_type = meta.get_type_knowledge().get_reduces_to(); + match reduced_type { + TypeReduction::Variable => { + let (tag, is_array) = environment.get_variable_or_break(name, file!(), line!()); + if *is_array{ + Known + } else{ + *tag + } + }, + TypeReduction::Signal => Unknown, + TypeReduction::Bus(_) => Unknown, + TypeReduction::Component(_) => *environment.get_component_or_break(name, file!(), line!()), + TypeReduction::Tag => Known, } } - ArrayInLine { values, .. } | Call { args: values, .. } => { + ArrayInLine { values, .. } + | Call { args: values, .. } + | BusCall { args: values, .. }=> { expression_iterator(values, Known, Unknown, environment) } UniformArray { value, dimension, .. } => { @@ -412,10 +448,10 @@ fn check_modified( initial_state: Environment, final_state: &mut Environment, modified_variables: &HashSet, -) -> bool{ +) -> bool { let mut modified = false; - for v in modified_variables{ - if initial_state.has_variable(v) && final_state.has_variable(v){ + for v in modified_variables { + if initial_state.has_variable(v) && final_state.has_variable(v) { let t_ini = initial_state.get_variable_or_break(v, file!(), line!()); let t_fin = final_state.get_mut_variable_or_break(v, file!(), line!()); if *t_ini != *t_fin{ @@ -429,19 +465,6 @@ fn check_modified( modified } -fn all_array_are_accesses(accesses: &[Access]) -> bool { - let mut i = 0; - let mut all_array_accesses = true; - while i < accesses.len() && all_array_accesses { - let aux = accesses.get(i).unwrap(); - if let Access::ComponentAccess(_) = aux { - all_array_accesses = false; - } - i = i + 1; - } - all_array_accesses -} - // ****************************** Expression utils ****************************** fn expression_iterator( values: &[Expression], @@ -449,48 +472,13 @@ fn expression_iterator( look_for: Tag, environment: &Environment, ) -> Tag { - let mut index = 0; - loop { - if index == values.len() { - break end_tag; - } - let index_tag = tag(&values[index], environment); + for value in values { + let index_tag = tag(value, environment); if index_tag == look_for { - break look_for; + return look_for; } - index += 1; - } -} - -// ****************************** AST simplification utils ****************************** -#[derive(Copy, Clone, Eq, PartialEq)] -enum Symbol { - Signal, - Component, - Variable, - SignalTag -} -fn simplify_symbol(environment: &Environment, name: &str, access: &[Access]) -> Symbol { - use Symbol::*; - if environment.has_variable(name) { - Variable - } else if environment.has_signal(name) { - let mut symbol = Signal; - for acc in access { - if let Access::ComponentAccess(_) = acc { - symbol = SignalTag; - } - } - symbol - } else { - let mut symbol = Component; - for acc in access { - if let Access::ComponentAccess(_) = acc { - symbol = Signal; - } - } - symbol } + end_tag } // ****************************** Early non-quadratic detection ****************************** @@ -502,45 +490,51 @@ fn is_non_quadratic(exp: &Expression, environment: &Environment) -> bool { fn unknown_index(exp: &Expression, environment: &Environment) -> bool { use Expression::*; use Tag::*; - let (init, rec) = match exp { - Number(..) => (false, vec![]), + match exp { + Number(..) => false, Variable { access, .. } => { let mut has_unknown_index = false; - let mut index = 0; - loop { - if index == access.len() || has_unknown_index { - break (has_unknown_index, vec![]); - } - if let Access::ArrayAccess(ex) = &access[index] { + for acc in access { + if let Access::ArrayAccess(ex) = acc { has_unknown_index = Unknown == tag(ex, environment); } - index += 1; + if has_unknown_index { + break; + } } + has_unknown_index + } + InfixOp { lhe, rhe, .. } => { + unknown_index(lhe.as_ref(), environment) || unknown_index(rhe.as_ref(), environment) + } + PrefixOp { rhe, .. } => { + unknown_index(rhe.as_ref(), environment) + } + ParallelOp { rhe, .. } => { + unknown_index(rhe.as_ref(), environment) } - InfixOp { lhe, rhe, .. } => (false, vec![lhe.as_ref(), rhe.as_ref()]), - PrefixOp { rhe, .. } => (false, vec![rhe.as_ref()]), - ParallelOp { rhe, .. } => (false, vec![rhe.as_ref()]), InlineSwitchOp { cond, if_true, if_false, .. } => { - (false, vec![cond.as_ref(), if_true.as_ref(), if_false.as_ref()]) + unknown_index(cond.as_ref(), environment) + || unknown_index(if_true.as_ref(), environment) + || unknown_index(if_false.as_ref(), environment) } - Call { args: exprs, .. } | ArrayInLine { values: exprs, .. } => { - let mut bucket = Vec::new(); + Call { args: exprs, .. } + | BusCall { args: exprs, .. } + | ArrayInLine { values: exprs, .. } + | Tuple { values: exprs, .. } => { + let mut has_unknown_index = false; for exp in exprs { - bucket.push(exp); + has_unknown_index = has_unknown_index || unknown_index(exp, environment); + if has_unknown_index { + break; + } } - (false, bucket) + has_unknown_index } - UniformArray{ value, dimension, .. } => (false, vec![value.as_ref(), dimension.as_ref()]), - _ => {unreachable!("Anonymous calls should not be reachable at this point."); } - }; - let mut has_unknown_index = init; - let mut index = 0; - loop { - if index == rec.len() || has_unknown_index { - break has_unknown_index; + UniformArray{ value, dimension, .. } => { + unknown_index(value.as_ref(), environment) || unknown_index(dimension.as_ref(), environment) } - has_unknown_index = unknown_index(&rec[index], environment); - index += 1; + _ => {unreachable!("Anonymous calls should not be reachable at this point."); } } } @@ -555,15 +549,17 @@ fn add_report( let mut report = Report::error("Typing error found".to_string(), error_code); let location = generate_file_location(meta.start, meta.end); let message = match error_code { + UnknownTemplateAssignment => "Assigments to signals within an unknown access to an array of components are not allowed".to_string(), + UnknownBus => "Parameters of a bus must be known during the constraint generation phase".to_string(), UnknownDimension => "The length of every array must be known during the constraint generation phase".to_string(), UnknownTemplate => "Every component instantiation must be resolved during the constraint generation phase. This component declaration uses a value that can be unknown during the constraint generation phase.".to_string(), NonValidTagAssignment => "Tags cannot be assigned to values that can be unknown during the constraint generation phase".to_string(), NonQuadratic => "Non-quadratic constraint was detected statically, using unknown index will cause the constraint to be non-quadratic".to_string(), UnreachableConstraints => "There are constraints depending on the value of the condition and it can be unknown during the constraint generation phase".to_string(), UnreachableTags => "There are tag assignments depending on the value of the condition and it can be unknown during the constraint generation phase".to_string(), - UnreachableSignals => "There are signal or component declarations depending on the value of the condition and it can be unknown during the constraint generation phase".to_string(), + UnreachableSignals => "There are signal, bus or component declarations depending on the value of the condition and it can be unknown during the constraint generation phase".to_string(), _ => panic!("Unimplemented error code") }; report.add_primary(location, file_id, message); reports.push(report); -} +} \ No newline at end of file diff --git a/type_analysis/src/check_types.rs b/type_analysis/src/check_types.rs index 5cc3c5682..e63c87dee 100644 --- a/type_analysis/src/check_types.rs +++ b/type_analysis/src/check_types.rs @@ -25,6 +25,11 @@ pub fn check_types( return Result::Err(errors); } + bus_level_analyses(program_archive, &mut errors); + if !errors.is_empty() { + return Result::Err(errors); + } + // Decorators template_level_decorators(program_archive, &mut errors); if !errors.is_empty() { @@ -36,6 +41,11 @@ pub fn check_types( return Result::Err(errors); } + bus_level_decorators(program_archive, &mut errors); + if !errors.is_empty() { + return Result::Err(errors); + } + // Type analysis let typing_result = type_check(program_archive); match typing_result { @@ -54,6 +64,11 @@ pub fn check_types( program_archive.remove_template(&name) } } + for name in program_archive.get_bus_names().clone() { + if !info.reached.contains(&name) { + program_archive.remove_bus(&name) + } + } } } @@ -91,33 +106,55 @@ fn template_level_analyses(program_archive: &ProgramArchive, reports: &mut Repor fn template_level_decorators( program_archive: &mut ProgramArchive, - _reports: &mut ReportCollection, + reports: &mut ReportCollection, ) { component_type_inference::inference(program_archive); + let program_archive2 = program_archive.clone(); for template_data in program_archive.get_mut_templates().values_mut() { - type_reduction::reduce_template(template_data); + reports.append(&mut type_reduction::reduce_template(template_data,&program_archive2)); } } fn function_level_analyses(program_archive: &ProgramArchive, reports: &mut ReportCollection) { let function_names = program_archive.get_function_names(); for function_data in program_archive.get_functions().values() { - let result_0 = free_of_template_elements(function_data, function_names); - let result_1 = all_paths_with_return_check(function_data); - if let Result::Err(mut functions_free_of_template_elements_reports) = result_0 { + let free_of_template_elements_result = free_of_template_elements(function_data, function_names); + let return_check_result = all_paths_with_return_check(function_data); + if let Result::Err(mut functions_free_of_template_elements_reports) = free_of_template_elements_result { reports.append(&mut functions_free_of_template_elements_reports); } - if let Result::Err(functions_all_paths_with_return_statement_report) = result_1 { + if let Result::Err(functions_all_paths_with_return_statement_report) = return_check_result { reports.push(functions_all_paths_with_return_statement_report); } } } fn function_level_decorators(program_archive: &mut ProgramArchive, reports: &mut ReportCollection) { + let program_archive2 = program_archive.clone(); for function_data in program_archive.get_mut_functions().values_mut() { let mut constant_handler_reports = constants_handler::handle_function_constants(function_data); - type_reduction::reduce_function(function_data); + reports.append(&mut type_reduction::reduce_function(function_data,&program_archive2)); + reports.append(&mut constant_handler_reports); + } +} + +fn bus_level_analyses(program_archive: &ProgramArchive, reports: &mut ReportCollection) { + let function_names = program_archive.get_function_names(); + for bus_data in program_archive.get_buses().values() { + let free_of_invalid_statements_result = free_of_invalid_statements(bus_data, function_names); + if let Result::Err(mut free_of_invalid_statements_reports) = free_of_invalid_statements_result { + reports.append(&mut free_of_invalid_statements_reports); + } + } +} + +fn bus_level_decorators(program_archive: &mut ProgramArchive, reports: &mut ReportCollection) { + let program_archive2 = program_archive.clone(); + for bus_data in program_archive.get_mut_buses().values_mut() { + let mut constant_handler_reports = + constants_handler::handle_bus_constants(bus_data); + reports.append(&mut type_reduction::reduce_bus(bus_data,&program_archive2)); reports.append(&mut constant_handler_reports); } } @@ -127,6 +164,12 @@ fn semantic_analyses( errors: &mut ReportCollection, warnings: &mut ReportCollection, ) { + for bus_name in program_archive.get_bus_names().iter() { + if let Result::Err(mut unknown_known_report) = + unknown_known_analysis(&bus_name, program_archive) { + errors.append(&mut unknown_known_report); + } + } for template_name in program_archive.get_template_names().iter() { if let Result::Err(mut unknown_known_report) = unknown_known_analysis(template_name, program_archive) { diff --git a/type_analysis/src/decorators/constants_handler.rs b/type_analysis/src/decorators/constants_handler.rs index bbffe78d7..b0ac54df5 100644 --- a/type_analysis/src/decorators/constants_handler.rs +++ b/type_analysis/src/decorators/constants_handler.rs @@ -4,7 +4,9 @@ use program_structure::error_code::ReportCode; use program_structure::error_definition::{Report, ReportCollection}; use program_structure::expression_builders::*; use program_structure::utils::environment::VarEnvironment; -use program_structure::{function_data::FunctionData, template_data::TemplateData}; +use program_structure::function_data::FunctionData; +use program_structure::template_data::TemplateData; +use program_structure::bus_data::BusData; use std::collections::HashSet; type Constants = VarEnvironment; @@ -38,6 +40,23 @@ pub fn _handle_template_constants(template: &mut TemplateData) -> ReportCollecti reports } +pub fn handle_bus_constants(bus: &mut BusData) -> ReportCollection { + let mut environment = Constants::new(); + let mut expression_holder = ExpressionHolder::new(); + for p in bus.get_name_of_params() { + environment.add_variable(p, true); + let meta = bus.get_body().get_meta().clone(); + let name = p.clone(); + let access = vec![]; + let expression = build_variable(meta, name, access); + expression_holder.add_variable(p, expression); + } + statement_constant_inference(bus.get_mut_body(), &mut environment); + let reports = statement_invariant_check(bus.get_body(), &mut environment); + expand_statement(bus.get_mut_body(), &mut expression_holder); + reports +} + // Set of functions used to infer the constant tag in variable declarations fn statement_constant_inference(stmt: &mut Statement, environment: &mut Constants) { use Statement::*; @@ -49,7 +68,7 @@ fn statement_constant_inference(stmt: &mut Statement, environment: &mut Constant InitializationBlock { initializations, .. } => { initialization_block_constant_inference(initializations, environment) } - While { stmt, .. } => while_stmt_constant_inference(stmt, environment), + While { stmt, .. } => while_constant_inference(stmt, environment), Block { stmts, .. } => block_constant_inference(stmts, environment), _ => {} } @@ -84,7 +103,7 @@ fn initialization_block_constant_inference( } } -fn while_stmt_constant_inference(stmt: &mut Statement, environment: &mut Constants) { +fn while_constant_inference(stmt: &mut Statement, environment: &mut Constants) { statement_constant_inference(stmt, environment) } @@ -224,6 +243,7 @@ fn has_constant_value(expr: &Expression, environment: &Constants) -> bool { Variable { name, .. } => variable(name, environment), ArrayInLine { .. } => array_inline(), UniformArray { .. } => uniform_array(), + BusCall { args, .. } => call(args, environment), _ => {unreachable!("Anonymous calls should not be reachable at this point."); } } } @@ -415,12 +435,22 @@ fn expand_expression(expr: Expression, environment: &ExpressionHolder) -> Expres expand_inline_switch_op(meta, *cond, *if_true, *if_false, environment) } Variable { meta, name, access } => expand_variable(meta, name, access, environment), + BusCall { meta, id, args } => expand_bus_call(meta,id,args,environment), _ => {unreachable!("Anonymous calls should not be reachable at this point."); } } } +fn expand_bus_call(meta: Meta, id: String, old_args: Vec, environment: &ExpressionHolder,) -> Expression { + let mut args = Vec::new(); + for expr in old_args { + let new_expression = expand_expression(expr, environment); + args.push(new_expression); + } + build_bus_call(meta, id, args) +} + fn expand_number(meta: Meta, value: BigInt) -> Expression { - build_number(meta, value) + build_number_without_field(meta, value) } fn expand_array( diff --git a/type_analysis/src/decorators/type_reduction.rs b/type_analysis/src/decorators/type_reduction.rs index 3e42a1027..37903b28d 100644 --- a/type_analysis/src/decorators/type_reduction.rs +++ b/type_analysis/src/decorators/type_reduction.rs @@ -1,87 +1,113 @@ +use program_structure::error_code::ReportCode; +use program_structure::error_definition::{Report, ReportCollection}; +use program_structure::program_archive::ProgramArchive; +use program_structure::wire_data::WireType; use program_structure::ast::*; +use program_structure::bus_data::BusData; use program_structure::environment::CircomEnvironment; use program_structure::function_data::FunctionData; use program_structure::template_data::TemplateData; -type Environment = CircomEnvironment<(), (), ()>; +type Environment = CircomEnvironment; -pub fn reduce_function(function_data: &mut FunctionData) { - let mut environment = CircomEnvironment::new(); +pub fn reduce_function(function_data: &mut FunctionData, program_archive : &ProgramArchive) -> ReportCollection { + let mut environment = Environment::new(); for param in function_data.get_name_of_params() { environment.add_variable(param, ()); } let body = function_data.get_mut_body(); - reduce_types_in_statement(body, &mut environment); + reduce_types_in_statement(body, &mut environment, program_archive) } -pub fn reduce_template(template_data: &mut TemplateData) { - let mut environment = CircomEnvironment::new(); +pub fn reduce_template(template_data: &mut TemplateData, program_archive : &ProgramArchive) -> ReportCollection { + let mut environment = Environment::new(); for param in template_data.get_name_of_params() { environment.add_variable(param, ()); } let body = template_data.get_mut_body(); - reduce_types_in_statement(body, &mut environment); + reduce_types_in_statement(body, &mut environment, program_archive) } -fn reduce_types_in_statement(stmt: &mut Statement, environment: &mut Environment) { +pub fn reduce_bus(bus_data: &mut BusData, program_archive : &ProgramArchive) -> ReportCollection { + let mut environment = Environment::new(); + for param in bus_data.get_name_of_params() { + environment.add_variable(param, ()); + } + let body = bus_data.get_mut_body(); + reduce_types_in_statement(body, &mut environment, program_archive) +} + +fn reduce_types_in_statement(stmt: &mut Statement, environment: &mut Environment, program_archive : &ProgramArchive) -> ReportCollection { use Statement::*; match stmt { Substitution { var, access, rhe, meta, .. } => { - reduce_types_in_substitution(var, access, environment, rhe, meta) + reduce_types_in_substitution(var, access, environment, rhe, meta, program_archive) } Declaration { name, xtype, dimensions, .. } => { - reduce_types_in_declaration(xtype, name, dimensions, environment) + reduce_types_in_declaration(xtype, name, dimensions, environment,program_archive) } - While { cond, stmt, .. } => reduce_types_in_while(cond, stmt, environment), - Block { stmts, .. } => reduce_types_in_vec_of_statements(stmts, environment), + While { cond, stmt, .. } => reduce_types_in_while(cond, stmt, environment,program_archive), + Block { stmts, .. } => reduce_types_in_vec_of_statements(stmts, environment,program_archive), InitializationBlock { initializations, .. } => { - reduce_types_in_vec_of_statements(initializations, environment) + reduce_types_in_vec_of_statements(initializations, environment,program_archive) } IfThenElse { cond, if_case, else_case, .. } => { - reduce_types_in_conditional(cond, if_case, else_case, environment) + reduce_types_in_conditional(cond, if_case, else_case, environment,program_archive) } LogCall { args, .. } => { - reduce_types_in_log_call(args, environment) + reduce_types_in_log_call(args, environment,program_archive) }, - Assert { arg, .. } => reduce_types_in_expression(arg, environment), - Return { value, .. } => reduce_types_in_expression(value, environment), + Assert { arg, .. } => reduce_types_in_expression(arg, environment,program_archive), + Return { value, .. } => reduce_types_in_expression(value, environment,program_archive), ConstraintEquality { lhe, rhe, .. } => { - reduce_types_in_constraint_equality(lhe, rhe, environment) + reduce_types_in_constraint_equality(lhe, rhe, environment,program_archive) } MultSubstitution { .. } => unreachable!(), UnderscoreSubstitution { rhe, .. } => { - reduce_types_in_expression(rhe, environment); + reduce_types_in_expression(rhe, environment,program_archive) }, } } -fn reduce_types_in_log_call(args: &mut Vec, environment: &Environment){ +fn reduce_types_in_log_call(args: &mut Vec, environment: &Environment, program_archive : &ProgramArchive) + -> ReportCollection { + let mut reports = Vec::new(); for arg in args { if let LogArgument::LogExp(exp) = arg { - reduce_types_in_expression(exp, environment); + reports.append(&mut reduce_types_in_expression(exp, environment, program_archive)); } } + reports } -fn reduce_types_in_expression(expression: &mut Expression, environment: &Environment) { +fn reduce_types_in_expression(expression: &mut Expression, environment: &Environment, program_archive : &ProgramArchive) + -> ReportCollection { use Expression::*; match expression { Variable { name, access, meta, .. } => { - reduce_types_in_variable(name, environment, access, meta) + reduce_types_in_variable(name, environment, access, meta,program_archive) } - InfixOp { lhe, rhe, .. } => reduce_types_in_infix(lhe, rhe, environment), - PrefixOp { rhe, .. } => reduce_types_in_expression(rhe, environment), - ParallelOp { rhe, .. } => reduce_types_in_expression(rhe, environment), + InfixOp { lhe, rhe, .. } => reduce_types_in_infix(lhe, rhe, environment,program_archive), + PrefixOp { rhe, .. } => reduce_types_in_expression(rhe, environment,program_archive), + ParallelOp { rhe, .. } => reduce_types_in_expression(rhe, environment,program_archive), InlineSwitchOp { cond, if_true, if_false, .. } => { - reduce_types_in_inline_switch(cond, if_true, if_false, environment) + reduce_types_in_inline_switch(cond, if_true, if_false, environment,program_archive) } - Call { args, .. } => reduce_types_in_vec_of_expressions(args, environment), - ArrayInLine { values, .. } => reduce_types_in_vec_of_expressions(values, environment), + Call { args, id, meta, .. } => { + meta.get_mut_type_knowledge().set_reduces_to(TypeReduction::Component(Some(id.clone()))); + reduce_types_in_vec_of_expressions(args, environment,program_archive) + }, + ArrayInLine { values, .. } => reduce_types_in_vec_of_expressions(values, environment,program_archive), UniformArray { value, dimension, .. } => { - reduce_types_in_expression(value, environment); - reduce_types_in_expression(dimension, environment); + let mut reports = reduce_types_in_expression(value, environment,program_archive); + reports.append(&mut reduce_types_in_expression(dimension, environment,program_archive)); + reports } - Number(..) => {} + Number(..) => { Vec::new() } + BusCall { args, meta, id, .. } => { + meta.get_mut_type_knowledge().set_reduces_to(TypeReduction::Bus(Some(id.clone()))); + reduce_types_in_vec_of_expressions(args, environment,program_archive) + }, _ => {unreachable!("Anonymous calls should not be reachable at this point."); } } } @@ -90,9 +116,11 @@ fn reduce_types_in_constraint_equality( lhe: &mut Expression, rhe: &mut Expression, environment: &mut Environment, -) { - reduce_types_in_expression(lhe, environment); - reduce_types_in_expression(rhe, environment); + program_archive : &ProgramArchive +) -> ReportCollection{ + let mut reports = reduce_types_in_expression(lhe, environment,program_archive); + reports.append(&mut reduce_types_in_expression(rhe, environment,program_archive)); + reports } fn reduce_types_in_declaration( @@ -100,36 +128,61 @@ fn reduce_types_in_declaration( name: &str, dimensions: &mut [Expression], environment: &mut Environment, -) { + program_archive : &ProgramArchive +) -> ReportCollection { use VariableType::*; if *xtype == Var { environment.add_variable(name, ()); } else if *xtype == Component || *xtype == AnonymousComponent { - environment.add_component(name, ()); + let mut typ = TypeKnowledge::default(); + typ.set_reduces_to(TypeReduction::Component(None)); + environment.add_component(name, typ); + } else if let Bus(bname,_,_) = xtype.clone(){ + let mut typ = TypeKnowledge::default(); + typ.set_reduces_to(TypeReduction::Bus(Some(bname))); + environment.add_intermediate_bus(name, typ) } else { environment.add_intermediate(name, ()); } - reduce_types_in_vec_of_expressions(dimensions, environment); + reduce_types_in_vec_of_expressions(dimensions, environment,program_archive) } -fn reduce_types_in_substitution( +fn reduce_types_in_substitution ( name: &str, access: &mut [Access], - environment: &Environment, + environment: &mut Environment, expr: &mut Expression, meta: &mut Meta, -) { - reduce_types_in_variable(name, environment, access, meta); - reduce_types_in_expression(expr, environment); + program_archive : &ProgramArchive +) -> ReportCollection { + let mut reports = reduce_types_in_variable(name, environment, access, meta,program_archive); + reports.append(&mut reduce_types_in_expression(expr, environment,program_archive)); + let mut is_simple_component = true; + for a in access{ + if let Access::ComponentAccess(_) = a { + is_simple_component = false; + } + } + if is_simple_component { + let xtype = environment.get_mut_component(name); + if xtype.is_some() && xtype.as_ref().unwrap().is_initialized() { + let reduced_type = expr.get_meta().get_type_knowledge().get_reduces_to(); + xtype.unwrap().set_reduces_to(reduced_type); + } + } + reports } fn reduce_types_in_while( cond: &mut Expression, stmt: &mut Statement, environment: &mut Environment, -) { - reduce_types_in_expression(cond, environment); - reduce_types_in_statement(stmt, environment); + program_archive : &ProgramArchive +)-> ReportCollection{ + let mut reports = Vec::new(); + reports.append(&mut reduce_types_in_expression(cond, environment,program_archive)); + reports.append(&mut reduce_types_in_statement(stmt, environment, program_archive)); + reports } fn reduce_types_in_conditional( @@ -137,51 +190,98 @@ fn reduce_types_in_conditional( if_branch: &mut Statement, else_branch: &mut Option>, environment: &mut Environment, -) { - reduce_types_in_expression(cond, environment); - reduce_types_in_statement(if_branch, environment); + program_archive : &ProgramArchive +) -> ReportCollection { + let mut reports = Vec::new(); + reports.append(&mut reduce_types_in_expression(cond, environment,program_archive)); + reports.append(&mut reduce_types_in_statement(if_branch, environment,program_archive)); if let Option::Some(else_stmt) = else_branch { - reduce_types_in_statement(else_stmt, environment); + reports.append(&mut reduce_types_in_statement(else_stmt, environment,program_archive)); } + reports } -fn reduce_types_in_vec_of_statements(vec: &mut [Statement], environment: &mut Environment) { +fn reduce_types_in_vec_of_statements(vec: &mut [Statement], environment: &mut Environment, program_archive : &ProgramArchive) + -> ReportCollection { + let mut reports = Vec::new(); for stmt in vec { - reduce_types_in_statement(stmt, environment); + reports.append(&mut reduce_types_in_statement(stmt, environment,program_archive)); } + reports } fn reduce_types_in_variable( - name: &str, + oname: &str, environment: &Environment, access: &mut [Access], meta: &mut Meta, -) { + program_archive : &ProgramArchive +) -> ReportCollection { use Access::*; use TypeReduction::*; - let mut reduction = if environment.has_signal(name) { + let mut reports = Vec::new(); + let mut reduction = if environment.has_signal(oname) { Signal - } else if environment.has_component(name) { - Component + } else if environment.has_component(oname) { + environment.get_component(oname).unwrap().get_reduces_to() + } else if environment.has_bus(oname){ + environment.get_bus(oname).unwrap().get_reduces_to() } else { Variable }; for acc in access { - if let ArrayAccess(exp) = acc { - reduce_types_in_expression(exp, environment) - } else if reduction == Signal{ - reduction = Tag; - } else { - reduction = Signal; + match acc { + ComponentAccess(name) => { + match reduction{ + Variable => {}, //Check type will return the corresponding error. + Component(ref comp) => { + if let Some(comp) = comp { + let template = program_archive.get_template_data(comp.as_str()); + let wire = template.get_inputs().get(name).or(template.get_outputs().get(name)); + if wire.is_some() { + match wire.unwrap().get_type(){ + WireType::Signal => reduction = Signal, + WireType::Bus(new_name) => reduction = Bus(Some(new_name)), + }//If it is not a signal or a bus, it is expected to be tag. + } else {//Then, type_check will finally check it. + name_not_found_in_component_error(name.clone(), oname.to_string(), meta,&mut reports); + return reports; + } + } + }, + Bus(ref b) => { + if let Some(b) = b { + let busdata = program_archive.get_bus_data(b.as_str()); + if let Some(wire) = busdata.get_fields().get(name){ + + match wire.get_type(){ + WireType::Signal => reduction = Signal, + WireType::Bus(new_name) => reduction = Bus(Some(new_name)), + } + } else { + reduction = Tag; + } + } + }, + Signal => reduction = Tag, + Tag => {}, + } + }, + ArrayAccess(exp) => { + reports.append(&mut reduce_types_in_expression(exp, environment,program_archive)); + }, } } meta.get_mut_type_knowledge().set_reduces_to(reduction); + reports } -fn reduce_types_in_infix(lhe: &mut Expression, rhe: &mut Expression, environment: &Environment) { - reduce_types_in_expression(lhe, environment); - reduce_types_in_expression(rhe, environment); +fn reduce_types_in_infix(lhe: &mut Expression, rhe: &mut Expression, environment: &Environment, program_archive : &ProgramArchive) -> ReportCollection{ + let mut reports = Vec::new(); + reports.append(&mut reduce_types_in_expression(lhe, environment, program_archive)); + reports.append(&mut reduce_types_in_expression(rhe, environment, program_archive)); + reports } fn reduce_types_in_inline_switch( @@ -189,14 +289,38 @@ fn reduce_types_in_inline_switch( if_true: &mut Expression, if_false: &mut Expression, environment: &Environment, -) { - reduce_types_in_expression(cond, environment); - reduce_types_in_expression(if_true, environment); - reduce_types_in_expression(if_false, environment); + program_archive : &ProgramArchive +) -> ReportCollection { + let mut reports = Vec::new(); + reports.append(&mut reduce_types_in_expression(cond, environment,program_archive)); + reports.append(&mut reduce_types_in_expression(if_true, environment,program_archive)); + reports.append(&mut reduce_types_in_expression(if_false, environment,program_archive)); + reports } -fn reduce_types_in_vec_of_expressions(vec: &mut [Expression], environment: &Environment) { +fn reduce_types_in_vec_of_expressions(vec: &mut [Expression], environment: &Environment, program_archive : &ProgramArchive) -> ReportCollection { + let mut reports = Vec::new(); for expr in vec { - reduce_types_in_expression(expr, environment); + reports.append(& mut reduce_types_in_expression(expr, environment,program_archive)); } + reports } + +// Errors +// fn name_not_found_in_bus_error(signal: String, what: String, meta: &Meta, reports: &mut ReportCollection) { +// let message = "Bus or signal not defined in bus".to_string(); +// let error_code = ReportCode::InvalidSignalAccessInBus; +// let mut report = Report::error(message, error_code); +// let message = signal + &" is not defined in ".to_string() + what.as_str(); +// report.add_primary(meta.file_location(), meta.get_file_id(), message); +// reports.push(report); +// } + +fn name_not_found_in_component_error(signal: String, what: String, meta: &Meta, reports: &mut ReportCollection) { + let message = "Bus or signal not defined in component".to_string(); + let error_code = ReportCode::InvalidSignalAccess; + let mut report = Report::error(message, error_code); + let message = signal + &" is not defined in ".to_string() + what.as_str(); + report.add_primary(meta.file_location(), meta.get_file_id(), message); + reports.push(report); +} \ No newline at end of file