diff --git a/src/protocol/parser/type_table.rs b/src/protocol/parser/type_table.rs index 280ebb993a6cd34997b1508fed97a023fb73e910..babe84e1d9f86672c9f93a3653bcbb2fd72c5853 100644 --- a/src/protocol/parser/type_table.rs +++ b/src/protocol/parser/type_table.rs @@ -341,6 +341,7 @@ pub struct StructMonomorphField { /// Union monomorph pub struct UnionMonomorph { pub variants: Vec, + pub tag_size: usize, // copied from `UnionType` upon monomorph construction. // note that the stack size is in the `TypeMonomorph` struct. This size and // alignment will include the size of the union tag. // @@ -397,7 +398,7 @@ pub struct TupleMonomorphMember { /// polymorphic variable). struct MonomorphKey { parts: Vec, - in_use: Vec, + in_use: Vec, // TODO: @Performance, limit num args and use two `u64` as bitflags or something } use std::hash::*; @@ -563,7 +564,6 @@ impl MonomorphTable { // Programmer note: keep this struct free of dynamically allocated memory #[derive(Clone)] struct TypeLoopBreadcrumb { - definition_id: DefinitionId, // TODO: Check if it can be removed? monomorph_idx: i32, next_member: u32, next_embedded: u32, // for unions, the index into the variant's embedded types @@ -571,11 +571,10 @@ struct TypeLoopBreadcrumb { #[derive(Clone)] struct MemoryBreadcrumb { - definition_id: DefinitionId, monomorph_idx: i32, - next_member: usize, - next_embedded: usize, - first_size_alignment_idx: usize, + next_member: u32, + next_embedded: u32, + first_size_alignment_idx: u32, } #[derive(Debug, PartialEq, Eq)] @@ -592,7 +591,6 @@ enum MemoryLayoutResult { // TODO: @Optimize, initial memory-unoptimized implementation struct TypeLoopEntry { - definition_id: DefinitionId, monomorph_idx: i32, is_union: bool, } @@ -712,6 +710,7 @@ impl TypeTable { /// Returns the index into the monomorph type array if the procedure type /// already has a (reserved) monomorph. + /// FIXME: This really shouldn't be called from within the runtime. See UnsafeCell in MonomorphTable #[inline] pub(crate) fn get_procedure_monomorph_index(&self, definition_id: &DefinitionId, type_parts: &[ConcreteTypePart]) -> Option { let base_type = self.type_lookup.get(definition_id).unwrap(); @@ -774,7 +773,6 @@ impl TypeTable { // Doesn't exist, so instantiate a monomorph and determine its memory // layout. self.detect_and_resolve_type_loops_for(modules, heap, concrete_type)?; - debug_assert_eq!(self.encountered_types[0].definition_id, definition_id); let mono_idx = self.encountered_types[0].monomorph_idx; self.lay_out_memory_for_encountered_types(arch); @@ -1043,7 +1041,7 @@ impl TypeTable { Self::check_poly_args_collision(modules, ctx, root_id, &definition.poly_vars)?; // Construct internal representation of component - // FIXME: Marking used polymorphic variables on procedures requires + // TODO: Marking used polymorphic variables on procedures requires // making sure that each is used in the body. For now, mark them all // as required. let mut poly_vars = Self::create_polymorphic_variables(&definition.poly_vars); @@ -1209,8 +1207,6 @@ impl TypeTable { // we throw an error). If there are no type loops or they are all // resolvable then we end up with a list of `encountered_types`. These // are then used by `lay_out_memory_for_encountered_types`. - use DefinedTypeVariant as DTV; - debug_assert!(self.type_loop_breadcrumbs.is_empty()); debug_assert!(self.type_loops.is_empty()); debug_assert!(self.encountered_types.is_empty()); @@ -1229,82 +1225,74 @@ impl TypeTable { let breadcrumb_idx = self.type_loop_breadcrumbs.len() - 1; let mut breadcrumb = self.type_loop_breadcrumbs[breadcrumb_idx].clone(); - // TODO: Also don't need DefinitionId here. So then only need mono_index, so then just - // do a switch on the monomorph, not on the base type. - let resolve_result = if breadcrumb.definition_id.is_invalid() { - // We're dealing with a tuple - let monomorph = self.mono_lookup.get(breadcrumb.monomorph_idx).variant.as_tuple(); - let num_members = monomorph.members.len() as u32; - let mut tuple_result = TypeLoopResult::TypeExists; + let monomorph = self.mono_lookup.get(breadcrumb.monomorph_idx); + let resolve_result = match &monomorph.variant { + MonomorphVariant::Enum => { + TypeLoopResult::TypeExists + }, + MonomorphVariant::Union(monomorph) => { + let num_variants = monomorph.variants.len() as u32; + let mut union_result = TypeLoopResult::TypeExists; + + 'member_loop: while breadcrumb.next_member < num_variants { + let mono_variant = &monomorph.variants[breadcrumb.next_member as usize]; + let num_embedded = mono_variant.embedded.len() as u32; + + while breadcrumb.next_embedded < num_embedded { + let mono_embedded = &mono_variant.embedded[breadcrumb.next_embedded as usize]; + union_result = self.check_member_for_type_loops(&mono_embedded.concrete_type); + + if union_result != TypeLoopResult::TypeExists { + // In type loop or new breadcrumb pushed, so + // break out of the resolving loop + break 'member_loop; + } - while breadcrumb.next_member < num_members { - let tuple_member = &monomorph.members[breadcrumb.next_member as usize]; - tuple_result = self.check_member_for_type_loops(&tuple_member.concrete_type); + breadcrumb.next_embedded += 1; + } - if tuple_result != TypeLoopResult::TypeExists { - break; + breadcrumb.next_embedded = 0; + breadcrumb.next_member += 1 } - breadcrumb.next_member += 1; - } - - tuple_result - } else { - let poly_type = self.type_lookup.get(&breadcrumb.definition_id).unwrap(); - match &poly_type.definition { - DTV::Enum(_) => { - TypeLoopResult::TypeExists - }, - DTV::Union(definition) => { - let monomorph = self.mono_lookup.get(breadcrumb.monomorph_idx).variant.as_union(); - let num_variants = monomorph.variants.len() as u32; - - let mut union_result = TypeLoopResult::TypeExists; - - 'member_loop: while breadcrumb.next_member < num_variants { - let mono_variant = &monomorph.variants[breadcrumb.next_member as usize]; - let num_embedded = mono_variant.embedded.len() as u32; + union_result + }, + MonomorphVariant::Struct(monomorph) => { + let num_fields = monomorph.fields.len() as u32; + + let mut struct_result = TypeLoopResult::TypeExists; + while breadcrumb.next_member < num_fields { + let mono_field = &monomorph.fields[breadcrumb.next_member as usize]; + struct_result = self.check_member_for_type_loops(&mono_field.concrete_type); + + if struct_result != TypeLoopResult::TypeExists { + // Type loop or breadcrumb pushed, so break out of + // the resolving loop + break; + } - while breadcrumb.next_embedded < num_embedded { - let mono_embedded = &mono_variant.embedded[breadcrumb.next_embedded as usize]; - union_result = self.check_member_for_type_loops(&mono_embedded.concrete_type); + breadcrumb.next_member += 1; + } - if union_result != TypeLoopResult::TypeExists { - // In type loop or new breadcrumb pushed, so - // break out of the resolving loop - break 'member_loop; - } + struct_result + }, + MonomorphVariant::Procedure(_) => unreachable!(), + MonomorphVariant::Tuple(monomorph) => { + let num_members = monomorph.members.len() as u32; + let mut tuple_result = TypeLoopResult::TypeExists; - breadcrumb.next_embedded += 1; - } + while breadcrumb.next_member < num_members { + let tuple_member = &monomorph.members[breadcrumb.next_member as usize]; + tuple_result = self.check_member_for_type_loops(&tuple_member.concrete_type); - breadcrumb.next_embedded = 0; - breadcrumb.next_member += 1 + if tuple_result != TypeLoopResult::TypeExists { + break; } - union_result - }, - DTV::Struct(definition) => { - let monomorph = self.mono_lookup.get(breadcrumb.monomorph_idx).variant.as_struct(); - let num_fields = monomorph.fields.len() as u32; - - let mut struct_result = TypeLoopResult::TypeExists; - while breadcrumb.next_member < num_fields { - let mono_field = &monomorph.fields[breadcrumb.next_member as usize]; - struct_result = self.check_member_for_type_loops(&mono_field.concrete_type); - - if struct_result != TypeLoopResult::TypeExists { - // Type loop or breadcrumb pushed, so break out of - // the resolving loop - break; - } - - breadcrumb.next_member += 1; - } + breadcrumb.next_member += 1; + } - struct_result - }, - DTV::Function(_) | DTV::Component(_) => unreachable!(), + tuple_result } }; @@ -1332,14 +1320,13 @@ impl TypeTable { let breadcrumb = &mut self.type_loop_breadcrumbs[breadcrumb_idx]; let mut is_union = false; - let entry = self.type_lookup.get_mut(&breadcrumb.definition_id).unwrap(); + let monomorph = self.mono_lookup.get_mut(breadcrumb.monomorph_idx); // TODO: Match on monomorph directly here - match &mut entry.definition { - DTV::Union(definition) => { + match &mut monomorph.variant { + MonomorphVariant::Union(monomorph) => { // Mark the currently processed variant as requiring heap // allocation, then advance the *embedded* type. The loop above // will then take care of advancing it to the next *member*. - let monomorph = self.mono_lookup.get_mut(breadcrumb.monomorph_idx).variant.as_union_mut(); let variant = &mut monomorph.variants[breadcrumb.next_member as usize]; variant.lives_on_heap = true; breadcrumb.next_embedded += 1; @@ -1350,7 +1337,6 @@ impl TypeTable { } loop_members.push(TypeLoopEntry{ - definition_id: breadcrumb.definition_id, monomorph_idx: breadcrumb.monomorph_idx, is_union }); @@ -1381,20 +1367,14 @@ impl TypeTable { // a type loop. fn type_loop_source_span_and_message<'a>( modules: &'a [Module], heap: &Heap, mono_lookup: &MonomorphTable, - defined_type: &DefinedType, monomorph_idx: i32, index_in_loop: usize + definition_id: DefinitionId, monomorph_idx: i32, index_in_loop: usize ) -> (&'a InputSource, InputSpan, String) { // Note: because we will discover the type loop the *first* time we // instantiate a monomorph with the provided polymorphic arguments // (not all arguments are actually used in the type). We don't have // to care about a second instantiation where certain unused // polymorphic arguments are different. - // TODO: Also remove match on defined_type, replace with mono lookup - let monomorph_type = match &defined_type.definition { - DTV::Union(definition) => &mono_lookup.get(monomorph_idx).concrete_type, - DTV::Struct(definition) => &mono_lookup.get(monomorph_idx).concrete_type, - DTV::Enum(_) | DTV::Function(_) | DTV::Component(_) => - unreachable!(), // impossible to have an enum/procedure in a type loop - }; + let monomorph_type = &mono_lookup.get(monomorph_idx).concrete_type; let type_name = monomorph_type.display_name(&heap); let message = if index_in_loop == 0 { @@ -1410,7 +1390,7 @@ impl TypeTable { format!("which depends on the type '{}'", type_name) }; - let ast_definition = &heap[defined_type.ast_definition]; + let ast_definition = &heap[definition_id]; let ast_root_id = ast_definition.defined_in(); return ( @@ -1420,25 +1400,48 @@ impl TypeTable { ); } + fn retrieve_definition_id_if_possible(parts: &[ConcreteTypePart]) -> DefinitionId { + match &parts[0] { + ConcreteTypePart::Instance(v, _) | + ConcreteTypePart::Function(v, _) | + ConcreteTypePart::Component(v, _) => *v, + _ => DefinitionId::new_invalid(), + } + } + fn construct_type_loop_error(table: &TypeTable, type_loop: &TypeLoop, modules: &[Module], heap: &Heap) -> ParseError { - let first_entry = &type_loop.members[0]; - let first_type = table.type_lookup.get(&first_entry.definition_id).unwrap(); - let (first_module, first_span, first_message) = type_loop_source_span_and_message( - modules, heap, &table.mono_lookup, first_type, first_entry.monomorph_idx, 0 - ); - let mut parse_error = ParseError::new_error_at_span(first_module, first_span, first_message); + // Seek first entry to produce parse error. Then continue builder + // pattern. This is the error case so efficiency can go home. + let mut parse_error = None; + let mut next_member_index = 0; + while next_member_index < type_loop.members.len() { + let first_entry = &type_loop.members[next_member_index]; + next_member_index += 1; + + let first_definition_id = retrieve_definition_id_if_possible(&table.mono_lookup.get(first_entry.monomorph_idx).concrete_type.parts); + if first_definition_id.is_invalid() { + continue; + } + + let (first_module, first_span, first_message) = type_loop_source_span_and_message( + modules, heap, &table.mono_lookup, first_definition_id, first_entry.monomorph_idx, 0 + ); + parse_error = Some(ParseError::new_error_at_span(first_module, first_span, first_message)); + break; + } + + let mut parse_error = parse_error.unwrap(); // Loop above cannot have failed, because we must have a type loop, type loops cannot contain only unnamed types let mut error_counter = 1; - for member_idx in 1..type_loop.members.len() { + for member_idx in next_member_index..type_loop.members.len() { let entry = &type_loop.members[member_idx]; - if entry.definition_id.is_invalid() { - // Don't display the tuples - continue; + let definition_id = retrieve_definition_id_if_possible(&table.mono_lookup.get(entry.monomorph_idx).concrete_type.parts); + if definition_id.is_invalid() { + continue; // dont display tuples } - let entry_type = table.type_lookup.get(&first_entry.definition_id).unwrap(); let (module, span, message) = type_loop_source_span_and_message( - modules, heap, &table.mono_lookup, entry_type, entry.monomorph_idx, error_counter + modules, heap, &table.mono_lookup, definition_id, entry.monomorph_idx, error_counter ); parse_error = parse_error.with_info_at_span(module, span, message); error_counter += 1; @@ -1453,7 +1456,7 @@ impl TypeTable { for entry in &type_loop.members { if entry.is_union { - let monomorph = &self.mono_lookup.get(entry.monomorph_idx).variant.as_union(); + let monomorph = self.mono_lookup.get(entry.monomorph_idx).variant.as_union(); debug_assert!(!monomorph.variants.is_empty()); // otherwise it couldn't be part of the type loop let has_stack_variant = monomorph.variants.iter().any(|variant| !variant.lives_on_heap); if has_stack_variant { @@ -1544,24 +1547,30 @@ impl TypeTable { offset: 0 }); } - let monomorph_index = self.mono_lookup.insert_with_zero_size_and_alignment( + let mono_index = self.mono_lookup.insert_with_zero_size_and_alignment( definition_type, &[], MonomorphVariant::Tuple(TupleMonomorph{ members, }) ); - monomorph_index + mono_index }, CTP::Instance(_check_definition_id, _) => { debug_assert_eq!(definition_id, *_check_definition_id); // because this is how `definition_id` was determined let base_type = self.type_lookup.get_mut(&definition_id).unwrap(); let monomorph_index = match &mut base_type.definition { DTV::Enum(definition) => { - // TODO: Is this correct? + // The enum is a bit exceptional in that when we insert + // it we we will immediately set its size/alignment: + // there is nothing to compute here. + debug_assert!(definition.size != 0 && definition.alignment != 0); let mono_index = self.mono_lookup.insert_with_zero_size_and_alignment( definition_type, &base_type.poly_vars, MonomorphVariant::Enum ); + let mono_type = self.mono_lookup.get_mut(mono_index); + mono_type.size = definition.size; + mono_type.alignment = definition.alignment; mono_index }, @@ -1590,6 +1599,7 @@ impl TypeTable { definition_type, &base_type.poly_vars, MonomorphVariant::Union(UnionMonomorph{ variants: mono_variants, + tag_size: definition.tag_size, heap_size: 0, heap_alignment: 0 }) @@ -1628,13 +1638,11 @@ impl TypeTable { }; self.encountered_types.push(TypeLoopEntry{ - definition_id, monomorph_idx: monomorph_index, is_union, }); self.type_loop_breadcrumbs.push(TypeLoopBreadcrumb{ - definition_id, monomorph_idx: monomorph_index, next_member: 0, next_embedded: 0, @@ -1721,7 +1729,6 @@ impl TypeTable { // to store intermediate size/alignment pairs until all members are // resolved. Note that this `size_alignment_stack` is NOT an // optimization, we're working around borrowing rules here. - use DefinedTypeVariant as DTV; // Just finished type loop detection, so we're left with the encountered // types only @@ -1734,7 +1741,6 @@ impl TypeTable { // were detecting type loops) let first_entry = &self.encountered_types[0]; self.memory_layout_breadcrumbs.push(MemoryBreadcrumb{ - definition_id: first_entry.definition_id, monomorph_idx: first_entry.monomorph_idx, next_member: 0, next_embedded: 0, @@ -1746,199 +1752,190 @@ impl TypeTable { let cur_breadcrumb_idx = self.memory_layout_breadcrumbs.len() - 1; let mut breadcrumb = self.memory_layout_breadcrumbs[cur_breadcrumb_idx].clone(); - // TODO: Same as above, replace lookup of base definition, should not - // be needed. - if breadcrumb.definition_id.is_invalid() { - // Handle tuple - let mono_tuple = self.mono_lookup.get(breadcrumb.monomorph_idx).variant.as_tuple(); - let num_members = mono_tuple.members.len(); - while breadcrumb.next_member < num_members { - let mono_member = &mono_tuple.members[breadcrumb.next_member]; - match self.get_memory_layout_or_breadcrumb(arch, &mono_member.concrete_type.parts) { - MemoryLayoutResult::TypeExists(size, alignment) => { - self.size_alignment_stack.push((size, alignment)); - }, - MemoryLayoutResult::PushBreadcrumb(new_breadcrumb) => { - self.memory_layout_breadcrumbs[cur_breadcrumb_idx] = breadcrumb; - self.memory_layout_breadcrumbs.push(new_breadcrumb); - continue 'breadcrumb_loop; - }, + let mono_type = self.mono_lookup.get(breadcrumb.monomorph_idx); + match &mono_type.variant { + MonomorphVariant::Enum => { + // Size should already be computed + if cfg!(debug_assertions) { + let mono_type = self.mono_lookup.get(breadcrumb.monomorph_idx); + debug_assert!(mono_type.size != 0 && mono_type.alignment != 0); } - - breadcrumb.next_member += 1; - } - - // If here then we can compute the memory layout of the tuple. - let mut cur_offset = 0; - let mut max_alignment = 1; - - let mono_info = self.mono_lookup.get_mut(breadcrumb.monomorph_idx); - let mono_tuple = mono_info.variant.as_tuple_mut(); - let mut size_alignment_index = breadcrumb.first_size_alignment_idx; - for member_index in 0..num_members { - let (member_size, member_alignment) = self.size_alignment_stack[size_alignment_index]; - align_offset_to(&mut cur_offset, member_alignment); - size_alignment_index += 1; - - let member = &mut mono_tuple.members[member_index]; - member.size = member_size; - member.alignment = member_alignment; - member.offset = cur_offset; - - cur_offset += member_size; - max_alignment = max_alignment.max(member_alignment); - } - - mono_info.size = cur_offset; - mono_info.alignment = max_alignment; - self.size_alignment_stack.truncate(breadcrumb.first_size_alignment_idx); - } else { - let poly_type = self.type_lookup.get(&breadcrumb.definition_id).unwrap(); - match &poly_type.definition { - DTV::Enum(definition) => { - // Size should already be computed - debug_assert!(definition.size != 0 && definition.alignment != 0); - let mono_type = self.mono_lookup.get_mut(breadcrumb.monomorph_idx); - mono_type.size = definition.size; - mono_type.alignment = definition.alignment; - }, - DTV::Union(definition) => { - // Retrieve size/alignment of each embedded type. We do not - // compute the offsets or total type sizes yet. - let mono_type = self.mono_lookup.get(breadcrumb.monomorph_idx).variant.as_union(); - let num_variants = mono_type.variants.len(); - while breadcrumb.next_member < num_variants { - let mono_variant = &mono_type.variants[breadcrumb.next_member]; - - if mono_variant.lives_on_heap { - // To prevent type loops we made this a heap- - // allocated variant. This implies we cannot - // compute sizes of members at this point. - } else { - let num_embedded = mono_variant.embedded.len(); - while breadcrumb.next_embedded < num_embedded { - let mono_embedded = &mono_variant.embedded[breadcrumb.next_embedded]; - match self.get_memory_layout_or_breadcrumb(arch, &mono_embedded.concrete_type.parts) { - MemoryLayoutResult::TypeExists(size, alignment) => { - self.size_alignment_stack.push((size, alignment)); - }, - MemoryLayoutResult::PushBreadcrumb(new_breadcrumb) => { - self.memory_layout_breadcrumbs[cur_breadcrumb_idx] = breadcrumb; - self.memory_layout_breadcrumbs.push(new_breadcrumb); - continue 'breadcrumb_loop; - } + }, + MonomorphVariant::Union(mono_type) => { + // Retrieve size/alignment of each embedded type. We do not + // compute the offsets or total type sizes yet. + let num_variants = mono_type.variants.len() as u32; + while breadcrumb.next_member < num_variants { + let mono_variant = &mono_type.variants[breadcrumb.next_member as usize]; + + if mono_variant.lives_on_heap { + // To prevent type loops we made this a heap- + // allocated variant. This implies we cannot + // compute sizes of members at this point. + } else { + let num_embedded = mono_variant.embedded.len() as u32; + while breadcrumb.next_embedded < num_embedded { + let mono_embedded = &mono_variant.embedded[breadcrumb.next_embedded as usize]; + match self.get_memory_layout_or_breadcrumb(arch, &mono_embedded.concrete_type.parts) { + MemoryLayoutResult::TypeExists(size, alignment) => { + self.size_alignment_stack.push((size, alignment)); + }, + MemoryLayoutResult::PushBreadcrumb(new_breadcrumb) => { + self.memory_layout_breadcrumbs[cur_breadcrumb_idx] = breadcrumb; + self.memory_layout_breadcrumbs.push(new_breadcrumb); + continue 'breadcrumb_loop; } - - breadcrumb.next_embedded += 1; } - } - breadcrumb.next_member += 1; - breadcrumb.next_embedded = 0; + breadcrumb.next_embedded += 1; + } } - // If here then we can at least compute the stack size of - // the type, we'll have to come back at the very end to - // fill in the heap size/alignment/offset of each heap- - // allocated variant. - let mut max_size = definition.tag_size; - let mut max_alignment = definition.tag_size; - - let poly_type = self.type_lookup.get_mut(&breadcrumb.definition_id).unwrap(); - let definition = poly_type.definition.as_union_mut(); - let mono_info = self.mono_lookup.get_mut(breadcrumb.monomorph_idx); - let mono_type = mono_info.variant.as_union_mut(); - let mut size_alignment_idx = breadcrumb.first_size_alignment_idx; - - for variant in &mut mono_type.variants { - // We're doing stack computations, so always start with - // the tag size/alignment. - let mut variant_offset = definition.tag_size; - let mut variant_alignment = definition.tag_size; - - if variant.lives_on_heap { - // Variant lives on heap, so just a pointer - let (ptr_size, ptr_align) = arch.pointer_size_alignment; - align_offset_to(&mut variant_offset, ptr_align); - - variant_offset += ptr_size; - variant_alignment = variant_alignment.max(ptr_align); - } else { - // Variant lives on stack, so walk all embedded - // types. - for embedded in &mut variant.embedded { - let (size, alignment) = self.size_alignment_stack[size_alignment_idx]; - embedded.size = size; - embedded.alignment = alignment; - size_alignment_idx += 1; - - align_offset_to(&mut variant_offset, alignment); - embedded.offset = variant_offset; - - variant_offset += size; - variant_alignment = variant_alignment.max(alignment); - } - }; - - max_size = max_size.max(variant_offset); - max_alignment = max_alignment.max(variant_alignment); - } + breadcrumb.next_member += 1; + breadcrumb.next_embedded = 0; + } - mono_info.size = max_size; - mono_info.alignment = max_alignment; - self.size_alignment_stack.truncate(breadcrumb.first_size_alignment_idx); - }, - DTV::Struct(definition) => { - // Retrieve size and alignment of each struct member. We'll - // compute the offsets once all of those are known - let mono_type = self.mono_lookup.get(breadcrumb.monomorph_idx).variant.as_struct(); - let num_fields = mono_type.fields.len(); - while breadcrumb.next_member < num_fields { - let mono_field = &mono_type.fields[breadcrumb.next_member]; - - match self.get_memory_layout_or_breadcrumb(arch, &mono_field.concrete_type.parts) { - MemoryLayoutResult::TypeExists(size, alignment) => { - self.size_alignment_stack.push((size, alignment)) - }, - MemoryLayoutResult::PushBreadcrumb(new_breadcrumb) => { - self.memory_layout_breadcrumbs[cur_breadcrumb_idx] = breadcrumb; - self.memory_layout_breadcrumbs.push(new_breadcrumb); - continue 'breadcrumb_loop; - }, + // If here then we can at least compute the stack size of + // the type, we'll have to come back at the very end to + // fill in the heap size/alignment/offset of each heap- + // allocated variant. + let mut max_size = mono_type.tag_size; + let mut max_alignment = mono_type.tag_size; + + let mono_info = self.mono_lookup.get_mut(breadcrumb.monomorph_idx); + let mono_type = mono_info.variant.as_union_mut(); + let mut size_alignment_idx = breadcrumb.first_size_alignment_idx as usize; + + for variant in &mut mono_type.variants { + // We're doing stack computations, so always start with + // the tag size/alignment. + let mut variant_offset = mono_type.tag_size; + let mut variant_alignment = mono_type.tag_size; + + if variant.lives_on_heap { + // Variant lives on heap, so just a pointer + let (ptr_size, ptr_align) = arch.pointer_size_alignment; + align_offset_to(&mut variant_offset, ptr_align); + + variant_offset += ptr_size; + variant_alignment = variant_alignment.max(ptr_align); + } else { + // Variant lives on stack, so walk all embedded + // types. + for embedded in &mut variant.embedded { + let (size, alignment) = self.size_alignment_stack[size_alignment_idx]; + embedded.size = size; + embedded.alignment = alignment; + size_alignment_idx += 1; + + align_offset_to(&mut variant_offset, alignment); + embedded.offset = variant_offset; + + variant_offset += size; + variant_alignment = variant_alignment.max(alignment); } + }; + + max_size = max_size.max(variant_offset); + max_alignment = max_alignment.max(variant_alignment); + } - breadcrumb.next_member += 1; + mono_info.size = max_size; + mono_info.alignment = max_alignment; + self.size_alignment_stack.truncate(breadcrumb.first_size_alignment_idx as usize); + }, + MonomorphVariant::Struct(mono_type) => { + // Retrieve size and alignment of each struct member. We'll + // compute the offsets once all of those are known + let num_fields = mono_type.fields.len() as u32; + while breadcrumb.next_member < num_fields { + let mono_field = &mono_type.fields[breadcrumb.next_member as usize]; + + match self.get_memory_layout_or_breadcrumb(arch, &mono_field.concrete_type.parts) { + MemoryLayoutResult::TypeExists(size, alignment) => { + self.size_alignment_stack.push((size, alignment)) + }, + MemoryLayoutResult::PushBreadcrumb(new_breadcrumb) => { + self.memory_layout_breadcrumbs[cur_breadcrumb_idx] = breadcrumb; + self.memory_layout_breadcrumbs.push(new_breadcrumb); + continue 'breadcrumb_loop; + }, } - // Compute offsets and size of total type - let mut cur_offset = 0; - let mut max_alignment = 1; + breadcrumb.next_member += 1; + } + + // Compute offsets and size of total type + let mut cur_offset = 0; + let mut max_alignment = 1; + + let mono_info = self.mono_lookup.get_mut(breadcrumb.monomorph_idx); + let mono_type = mono_info.variant.as_struct_mut(); + let mut size_alignment_idx = breadcrumb.first_size_alignment_idx as usize; - let mono_info = self.mono_lookup.get_mut(breadcrumb.monomorph_idx); - let mono_type = mono_info.variant.as_struct_mut(); - let mut size_alignment_idx = breadcrumb.first_size_alignment_idx; + for field in &mut mono_type.fields { + let (size, alignment) = self.size_alignment_stack[size_alignment_idx]; + field.size = size; + field.alignment = alignment; + size_alignment_idx += 1; - for field in &mut mono_type.fields { - let (size, alignment) = self.size_alignment_stack[size_alignment_idx]; - field.size = size; - field.alignment = alignment; - size_alignment_idx += 1; + align_offset_to(&mut cur_offset, alignment); + field.offset = cur_offset; - align_offset_to(&mut cur_offset, alignment); - field.offset = cur_offset; + cur_offset += size; + max_alignment = max_alignment.max(alignment); + } - cur_offset += size; - max_alignment = max_alignment.max(alignment); + mono_info.size = cur_offset; + mono_info.alignment = max_alignment; + self.size_alignment_stack.truncate(breadcrumb.first_size_alignment_idx as usize); + }, + MonomorphVariant::Procedure(_) => { + unreachable!(); + }, + MonomorphVariant::Tuple(mono_type) => { + let num_members = mono_type.members.len() as u32; + while breadcrumb.next_member < num_members { + let mono_member = &mono_type.members[breadcrumb.next_member as usize]; + match self.get_memory_layout_or_breadcrumb(arch, &mono_member.concrete_type.parts) { + MemoryLayoutResult::TypeExists(size, alignment) => { + self.size_alignment_stack.push((size, alignment)); + }, + MemoryLayoutResult::PushBreadcrumb(new_breadcrumb) => { + self.memory_layout_breadcrumbs[cur_breadcrumb_idx] = breadcrumb; + self.memory_layout_breadcrumbs.push(new_breadcrumb); + continue 'breadcrumb_loop; + }, } - mono_info.size = cur_offset; - mono_info.alignment = max_alignment; - self.size_alignment_stack.truncate(breadcrumb.first_size_alignment_idx); - }, - DTV::Function(_) | DTV::Component(_) => { - unreachable!(); + breadcrumb.next_member += 1; } - } + + // If here then we can compute the memory layout of the tuple. + let mut cur_offset = 0; + let mut max_alignment = 1; + + let mono_info = self.mono_lookup.get_mut(breadcrumb.monomorph_idx); + let mono_type = mono_info.variant.as_tuple_mut(); + let mut size_alignment_index = breadcrumb.first_size_alignment_idx as usize; + for member_index in 0..num_members { + let (member_size, member_alignment) = self.size_alignment_stack[size_alignment_index]; + align_offset_to(&mut cur_offset, member_alignment); + size_alignment_index += 1; + + let member = &mut mono_type.members[member_index as usize]; + member.size = member_size; + member.alignment = member_alignment; + member.offset = cur_offset; + + cur_offset += member_size; + max_alignment = max_alignment.max(member_alignment); + } + + mono_info.size = cur_offset; + mono_info.alignment = max_alignment; + self.size_alignment_stack.truncate(breadcrumb.first_size_alignment_idx as usize); + }, } // If here, then we completely layed out the current type. So move @@ -2051,11 +2048,10 @@ impl TypeTable { return MemoryLayoutResult::TypeExists(size, alignment); } else { return MemoryLayoutResult::PushBreadcrumb(MemoryBreadcrumb{ - definition_id: DefinitionId::new_invalid(), monomorph_idx: mono_index, next_member: 0, next_embedded: 0, - first_size_alignment_idx: self.size_alignment_stack.len(), + first_size_alignment_idx: self.size_alignment_stack.len() as u32, }) } }, @@ -2069,11 +2065,10 @@ impl TypeTable { return MemoryLayoutResult::TypeExists(size, alignment); } else { return MemoryLayoutResult::PushBreadcrumb(MemoryBreadcrumb{ - definition_id, monomorph_idx: mono_index, next_member: 0, next_embedded: 0, - first_size_alignment_idx: self.size_alignment_stack.len(), + first_size_alignment_idx: self.size_alignment_stack.len() as u32, }); } }, diff --git a/src/protocol/tests/utils.rs b/src/protocol/tests/utils.rs index 9228128e09d7617d1fcbf559333d26351d618326..84dde7c2d467633829f7e2df5863b42aee9524b7 100644 --- a/src/protocol/tests/utils.rs +++ b/src/protocol/tests/utils.rs @@ -924,8 +924,6 @@ impl<'a> ErrorTester<'a> { //------------------------------------------------------------------------------ fn has_equal_num_monomorphs(ctx: TestCtx, num: usize, definition_id: DefinitionId) -> (bool, usize) { - use DefinedTypeVariant::*; - // Again: inefficient, but its testing code let type_def = ctx.types.get_base_definition(&definition_id).unwrap(); let mut num_on_type = 0; @@ -947,10 +945,6 @@ fn has_equal_num_monomorphs(ctx: TestCtx, num: usize, definition_id: DefinitionI } fn has_monomorph(ctx: TestCtx, definition_id: DefinitionId, serialized_monomorph: &str) -> (Option, String) { - use DefinedTypeVariant::*; - - let type_def = ctx.types.get_base_definition(&definition_id).unwrap(); - // Note: full_buffer is just for error reporting let mut full_buffer = String::new(); let mut has_match = None;