diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 412082e089..3f3832ed02 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -1012,7 +1012,7 @@ fn struct_pointer_from_fields<'a, 'ctx, 'env, I>( input_pointer: PointerValue<'ctx>, values: I, ) where - I: Iterator)>, + I: Iterator, BasicValueEnum<'ctx>))>, { let struct_ptr = env .builder @@ -1024,13 +1024,13 @@ fn struct_pointer_from_fields<'a, 'ctx, 'env, I>( .into_pointer_value(); // Insert field exprs into struct_val - for (index, field_val) in values { + for (index, (field_layout, field_value)) in values { let field_ptr = env .builder .build_struct_gep(struct_ptr, index as u32, "field_struct_gep") .unwrap(); - env.builder.build_store(field_ptr, field_val); + store_roc_value(env, field_layout, field_ptr, field_value); } } @@ -1405,53 +1405,11 @@ fn build_wrapped_tag<'a, 'ctx, 'env>( reuse_allocation: Option>, parent: FunctionValue<'ctx>, ) -> BasicValueEnum<'ctx> { - let ctx = env.context; let builder = env.builder; let tag_id_layout = union_layout.tag_id_layout(); - // Determine types - let num_fields = arguments.len() + 1; - let mut field_types = Vec::with_capacity_in(num_fields, env.arena); - let mut field_vals = Vec::with_capacity_in(num_fields, env.arena); - - for (field_symbol, tag_field_layout) in arguments.iter().zip(tag_field_layouts.iter()) { - let (val, _val_layout) = load_symbol_and_layout(scope, field_symbol); - - let field_type = basic_type_from_layout(env, tag_field_layout); - - field_types.push(field_type); - - if let Layout::RecursivePointer = tag_field_layout { - debug_assert!(val.is_pointer_value()); - - // we store recursive pointers as `i64*` - let ptr = env.builder.build_bitcast( - val, - ctx.i64_type().ptr_type(AddressSpace::Generic), - "cast_recursive_pointer", - ); - - field_vals.push(ptr); - } else if matches!( - tag_field_layout, - Layout::Union(UnionLayout::NonRecursive(_)) - ) { - debug_assert!(val.is_pointer_value()); - - // We store non-recursive unions without any indirection. - let reified = env - .builder - .build_load(val.into_pointer_value(), "load_non_recursive"); - - field_vals.push(reified); - } else { - // this check fails for recursive tag unions, but can be helpful while debugging - // debug_assert_eq!(tag_field_layout, val_layout); - - field_vals.push(val); - } - } + let (field_types, field_values) = build_tag_fields(env, scope, tag_field_layouts, arguments); // Create the struct_type let raw_data_ptr = allocate_tag(env, parent, reuse_allocation, union_layout, tags); @@ -1475,7 +1433,7 @@ fn build_wrapped_tag<'a, 'ctx, 'env>( env, struct_type, opaque_struct_ptr, - field_vals.into_iter().enumerate(), + field_values.into_iter().enumerate(), ); raw_data_ptr.into() @@ -1484,7 +1442,7 @@ fn build_wrapped_tag<'a, 'ctx, 'env>( env, struct_type, raw_data_ptr, - field_vals.into_iter().enumerate(), + field_values.into_iter().enumerate(), ); tag_pointer_set_tag_id(env, tag_id, raw_data_ptr).into() @@ -1527,7 +1485,63 @@ pub fn entry_block_alloca_zerofill<'a, 'ctx, 'env>( result_alloca } -pub fn build_tag<'a, 'ctx, 'env>( +fn build_tag_field_value<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + value: BasicValueEnum<'ctx>, + tag_field_layout: Layout<'a>, +) -> BasicValueEnum<'ctx> { + if let Layout::RecursivePointer = tag_field_layout { + debug_assert!(value.is_pointer_value()); + + // we store recursive pointers as `i64*` + env.builder.build_bitcast( + value, + env.context.i64_type().ptr_type(AddressSpace::Generic), + "cast_recursive_pointer", + ) + } else if tag_field_layout.is_passed_by_reference() { + debug_assert!(value.is_pointer_value()); + + // NOTE: we rely on this being passed to `store_roc_value` so that + // the value is memcpy'd + value + } else { + // this check fails for recursive tag unions, but can be helpful while debugging + // debug_assert_eq!(tag_field_layout, val_layout); + + value + } +} + +fn build_tag_fields<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + scope: &Scope<'a, 'ctx>, + fields: &[Layout<'a>], + arguments: &[Symbol], +) -> ( + Vec<'a, BasicTypeEnum<'ctx>>, + Vec<'a, (Layout<'a>, BasicValueEnum<'ctx>)>, +) { + debug_assert_eq!(fields.len(), arguments.len()); + + let capacity = fields.len(); + let mut field_types = Vec::with_capacity_in(capacity, env.arena); + let mut field_values = Vec::with_capacity_in(capacity, env.arena); + + for (field_symbol, tag_field_layout) in arguments.iter().zip(fields.iter()) { + let field_type = basic_type_from_layout(env, tag_field_layout); + field_types.push(field_type); + + let raw_value = load_symbol(scope, field_symbol); + let field_value = build_tag_field_value(env, raw_value, *tag_field_layout); + + field_values.push((*tag_field_layout, field_value)); + } + + (field_types, field_values) +} + +fn build_tag<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, scope: &Scope<'a, 'ctx>, union_layout: &UnionLayout<'a>, @@ -1678,49 +1692,21 @@ pub fn build_tag<'a, 'ctx, 'env>( debug_assert_eq!(tag_id, 0); debug_assert_eq!(arguments.len(), fields.len()); - let ctx = env.context; - - // Determine types - let num_fields = arguments.len() + 1; - let mut field_types = Vec::with_capacity_in(num_fields, env.arena); - let mut field_vals = Vec::with_capacity_in(num_fields, env.arena); - - for (field_symbol, tag_field_layout) in arguments.iter().zip(fields.iter()) { - let val = load_symbol(scope, field_symbol); - - let field_type = basic_type_from_layout(env, tag_field_layout); - - field_types.push(field_type); - - if let Layout::RecursivePointer = tag_field_layout { - debug_assert!(val.is_pointer_value()); - - // we store recursive pointers as `i64*` - let ptr = env.builder.build_bitcast( - val, - ctx.i64_type().ptr_type(AddressSpace::Generic), - "cast_recursive_pointer", - ); - - field_vals.push(ptr); - } else { - // this check fails for recursive tag unions, but can be helpful while debugging - - field_vals.push(val); - } - } + let (field_types, field_values) = build_tag_fields(env, scope, fields, arguments); // Create the struct_type let data_ptr = reserve_with_refcount_union_as_block_of_memory(env, *union_layout, &[fields]); - let struct_type = ctx.struct_type(field_types.into_bump_slice(), false); + let struct_type = env + .context + .struct_type(field_types.into_bump_slice(), false); struct_pointer_from_fields( env, struct_type, data_ptr, - field_vals.into_iter().enumerate(), + field_values.into_iter().enumerate(), ); data_ptr.into() @@ -1744,56 +1730,22 @@ pub fn build_tag<'a, 'ctx, 'env>( debug_assert!(union_size == 2); - let ctx = env.context; - // Determine types - let num_fields = arguments.len() + 1; - let mut field_types = Vec::with_capacity_in(num_fields, env.arena); - let mut field_vals = Vec::with_capacity_in(num_fields, env.arena); - - debug_assert_eq!(arguments.len(), other_fields.len()); - - for (field_symbol, tag_field_layout) in arguments.iter().zip(other_fields.iter()) { - let val = load_symbol(scope, field_symbol); - - // Zero-sized fields have no runtime representation. - // The layout of the struct expects them to be dropped! - if !tag_field_layout.is_dropped_because_empty() { - let field_type = basic_type_from_layout(env, tag_field_layout); - - field_types.push(field_type); - - if let Layout::RecursivePointer = tag_field_layout { - debug_assert!(val.is_pointer_value()); - - // we store recursive pointers as `i64*` - let ptr = env.builder.build_bitcast( - val, - ctx.i64_type().ptr_type(AddressSpace::Generic), - "cast_recursive_pointer", - ); - - field_vals.push(ptr); - } else { - // this check fails for recursive tag unions, but can be helpful while debugging - // debug_assert_eq!(tag_field_layout, val_layout); - - field_vals.push(val); - } - } - } + let (field_types, field_values) = build_tag_fields(env, scope, other_fields, arguments); // Create the struct_type let data_ptr = allocate_tag(env, parent, reuse_allocation, union_layout, &[other_fields]); - let struct_type = ctx.struct_type(field_types.into_bump_slice(), false); + let struct_type = env + .context + .struct_type(field_types.into_bump_slice(), false); struct_pointer_from_fields( env, struct_type, data_ptr, - field_vals.into_iter().enumerate(), + field_values.into_iter().enumerate(), ); data_ptr.into() @@ -2491,6 +2443,8 @@ pub fn store_roc_value<'a, 'ctx, 'env>( value: BasicValueEnum<'ctx>, ) { if layout.is_passed_by_reference() { + debug_assert!(value.is_pointer_value()); + let align_bytes = layout.alignment_bytes(env.target_info); if align_bytes > 0 {