Merge remote-tracking branch 'origin/main' into linux64

This commit is contained in:
Richard Feldman 2023-07-11 11:30:02 -04:00
commit 1d5c5b25ec
No known key found for this signature in database
GPG key ID: F1F21AA5B1D9E43B
328 changed files with 11845 additions and 9244 deletions

View file

@ -120,7 +120,7 @@ impl<'a> LlvmAlignment<'a> for LayoutRepr<'a> {
.llvm_alignment_bytes(interner),
Builtin(builtin) => builtin.llvm_alignment_bytes(interner),
RecursivePointer(_) => interner.target_info().ptr_width() as u32,
Boxed(_) => interner.target_info().ptr_width() as u32,
Ptr(_) => interner.target_info().ptr_width() as u32,
}
}
}

View file

@ -34,10 +34,7 @@ pub fn call_bitcode_fn<'ctx>(
.try_as_basic_value()
.left()
.unwrap_or_else(|| {
panic!(
"LLVM error: Did not get return value from bitcode function {:?}",
fn_name
)
panic!("LLVM error: Did not get return value from bitcode function {fn_name:?}")
})
}
@ -49,7 +46,7 @@ pub fn call_void_bitcode_fn<'ctx>(
call_bitcode_fn_help(env, args, fn_name)
.try_as_basic_value()
.right()
.unwrap_or_else(|| panic!("LLVM error: Tried to call void bitcode function, but got return value from bitcode function, {:?}", fn_name))
.unwrap_or_else(|| panic!("LLVM error: Tried to call void bitcode function, but got return value from bitcode function, {fn_name:?}"))
}
fn call_bitcode_fn_help<'ctx>(
@ -63,7 +60,7 @@ fn call_bitcode_fn_help<'ctx>(
let fn_val = env
.module
.get_function(fn_name)
.unwrap_or_else(|| panic!("Unrecognized builtin function: {:?} - if you're working on the Roc compiler, do you need to rebuild the bitcode? See compiler/builtins/bitcode/README.md", fn_name));
.unwrap_or_else(|| panic!("Unrecognized builtin function: {fn_name:?} - if you're working on the Roc compiler, do you need to rebuild the bitcode? See compiler/builtins/bitcode/README.md"));
let call = env.builder.build_call(fn_val, &arguments, "call_builtin");
@ -225,7 +222,7 @@ fn build_transform_caller_help<'a, 'ctx>(
let kind_id = Attribute::get_named_enum_kind_id("alwaysinline");
debug_assert!(kind_id > 0);
let attr = env.context.create_enum_attribute(kind_id, 1);
let attr = env.context.create_enum_attribute(kind_id, 0);
function_value.add_attribute(AttributeLoc::Function, attr);
let entry = env.context.append_basic_block(function_value, "entry");
@ -378,9 +375,9 @@ fn build_rc_wrapper<'a, 'ctx>(
.to_symbol_string(symbol, &env.interns);
let fn_name = match rc_operation {
Mode::IncN => format!("{}_inc_n", fn_name),
Mode::Inc => format!("{}_inc", fn_name),
Mode::Dec => format!("{}_dec", fn_name),
Mode::IncN => format!("{fn_name}_inc_n"),
Mode::Inc => format!("{fn_name}_inc"),
Mode::Dec => format!("{fn_name}_dec"),
};
let function_value = match env.module.get_function(fn_name.as_str()) {
@ -408,7 +405,7 @@ fn build_rc_wrapper<'a, 'ctx>(
let kind_id = Attribute::get_named_enum_kind_id("alwaysinline");
debug_assert!(kind_id > 0);
let attr = env.context.create_enum_attribute(kind_id, 1);
let attr = env.context.create_enum_attribute(kind_id, 0);
function_value.add_attribute(AttributeLoc::Function, attr);
let entry = env.context.append_basic_block(function_value, "entry");
@ -497,7 +494,7 @@ pub fn build_eq_wrapper<'a, 'ctx>(
let kind_id = Attribute::get_named_enum_kind_id("alwaysinline");
debug_assert!(kind_id > 0);
let attr = env.context.create_enum_attribute(kind_id, 1);
let attr = env.context.create_enum_attribute(kind_id, 0);
function_value.add_attribute(AttributeLoc::Function, attr);
let entry = env.context.append_basic_block(function_value, "entry");
@ -598,7 +595,7 @@ pub fn build_compare_wrapper<'a, 'ctx>(
let kind_id = Attribute::get_named_enum_kind_id("alwaysinline");
debug_assert!(kind_id > 0);
let attr = env.context.create_enum_attribute(kind_id, 1);
let attr = env.context.create_enum_attribute(kind_id, 0);
function_value.add_attribute(AttributeLoc::Function, attr);
let entry = env.context.append_basic_block(function_value, "entry");

View file

@ -23,7 +23,7 @@ use inkwell::passes::{PassManager, PassManagerBuilder};
use inkwell::types::{
AnyType, BasicMetadataTypeEnum, BasicType, BasicTypeEnum, FunctionType, IntType, StructType,
};
use inkwell::values::BasicValueEnum::{self};
use inkwell::values::BasicValueEnum;
use inkwell::values::{
BasicMetadataValueEnum, CallSiteValue, FunctionValue, InstructionValue, IntValue, PointerValue,
StructValue,
@ -263,7 +263,7 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> {
let fn_val = self
.module
.get_function(intrinsic_name)
.unwrap_or_else(|| panic!("Unrecognized intrinsic function: {}", intrinsic_name));
.unwrap_or_else(|| panic!("Unrecognized intrinsic function: {intrinsic_name}"));
let mut arg_vals: Vec<BasicMetadataValueEnum> =
Vec::with_capacity_in(args.len(), self.arena);
@ -289,10 +289,7 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> {
let call = self.build_intrinsic_call(intrinsic_name, args);
call.try_as_basic_value().left().unwrap_or_else(|| {
panic!(
"LLVM error: Invalid call by name for intrinsic {}",
intrinsic_name
)
panic!("LLVM error: Invalid call by name for intrinsic {intrinsic_name}")
})
}
@ -505,17 +502,14 @@ pub fn module_from_builtins<'ctx>(
} => {
include_bytes!("../../../builtins/bitcode/builtins-windows-x86_64.bc")
}
_ => panic!(
"The zig builtins are not currently built for this target: {:?}",
target
),
_ => panic!("The zig builtins are not currently built for this target: {target:?}"),
}
};
let memory_buffer = MemoryBuffer::create_from_memory_range(bitcode_bytes, module_name);
let module = Module::parse_bitcode_from_buffer(&memory_buffer, ctx)
.unwrap_or_else(|err| panic!("Unable to import builtins bitcode. LLVM error: {:?}", err));
.unwrap_or_else(|err| panic!("Unable to import builtins bitcode. LLVM error: {err:?}"));
// Add LLVM intrinsics.
add_intrinsics(ctx, &module);
@ -773,7 +767,7 @@ pub fn build_exp_literal<'a, 'ctx>(
LayoutRepr::Builtin(Builtin::Int(int_width)) => {
int_with_precision(env, i128::from_ne_bytes(*bytes), int_width).into()
}
_ => panic!("Invalid layout for int literal = {:?}", layout),
_ => panic!("Invalid layout for int literal = {layout:?}"),
},
U128(bytes) => const_u128(env, u128::from_ne_bytes(*bytes)).into(),
@ -782,7 +776,7 @@ pub fn build_exp_literal<'a, 'ctx>(
LayoutRepr::Builtin(Builtin::Float(float_width)) => {
float_with_precision(env, *float, float_width)
}
_ => panic!("Invalid layout for float literal = {:?}", layout),
_ => panic!("Invalid layout for float literal = {layout:?}"),
},
Decimal(bytes) => {
@ -1071,14 +1065,14 @@ pub(crate) fn build_exp_expr<'a, 'ctx>(
)
.into(),
Reuse {
Tag {
arguments,
tag_layout: union_layout,
tag_id,
symbol,
..
reuse,
} => {
let reset = scope.load_symbol(symbol).into_pointer_value();
let reuse_ptr = reuse.map(|ru| scope.load_symbol(&ru.symbol).into_pointer_value());
build_tag(
env,
layout_interner,
@ -1086,63 +1080,11 @@ pub(crate) fn build_exp_expr<'a, 'ctx>(
union_layout,
*tag_id,
arguments,
Some(reset),
reuse_ptr,
parent,
)
}
Tag {
arguments,
tag_layout: union_layout,
tag_id,
..
} => build_tag(
env,
layout_interner,
scope,
union_layout,
*tag_id,
arguments,
None,
parent,
),
ExprBox { symbol } => {
let (value, layout) = scope.load_symbol_and_layout(symbol);
let basic_type =
basic_type_from_layout(env, layout_interner, layout_interner.get_repr(layout));
let allocation = reserve_with_refcount_help(
env,
basic_type,
layout_interner.stack_size(layout),
layout_interner.alignment_bytes(layout),
);
store_roc_value(
env,
layout_interner,
layout_interner.get_repr(layout),
allocation,
value,
);
allocation.into()
}
ExprUnbox { symbol } => {
let value = scope.load_symbol(symbol);
debug_assert!(value.is_pointer_value());
load_roc_value(
env,
layout_interner,
layout_interner.get_repr(layout),
value.into_pointer_value(),
"load_boxed_value",
)
}
Reset {
symbol,
update_mode,
@ -1172,7 +1114,12 @@ pub(crate) fn build_exp_expr<'a, 'ctx>(
env.builder.position_at_end(check_if_null);
env.builder.build_conditional_branch(
env.builder.build_is_null(tag_ptr, "is_tag_null"),
// have llvm optimizations clean this up
if layout_interner.is_nullable(layout) {
env.builder.build_is_null(tag_ptr, "is_tag_null")
} else {
env.context.bool_type().const_int(false as _, false)
},
cont_block,
check_if_unique,
);
@ -1182,8 +1129,14 @@ pub(crate) fn build_exp_expr<'a, 'ctx>(
let then_block = ctx.append_basic_block(parent, "then_reset");
let else_block = ctx.append_basic_block(parent, "else_decref");
let refcount_ptr =
PointerToRefcount::from_ptr_to_data(env, tag_pointer_clear_tag_id(env, tag_ptr));
let refcount_ptr = PointerToRefcount::from_ptr_to_data(
env,
if union_layout.stores_tag_id_in_pointer(env.target_info) {
tag_pointer_clear_tag_id(env, tag_ptr)
} else {
tag_ptr
},
);
let is_unique = match update_mode {
UpdateMode::InPlace => env.context.bool_type().const_int(1, false),
@ -1253,7 +1206,12 @@ pub(crate) fn build_exp_expr<'a, 'ctx>(
env.builder.position_at_end(check_if_null);
env.builder.build_conditional_branch(
env.builder.build_is_null(tag_ptr, "is_tag_null"),
// have llvm optimizations clean this up
if layout_interner.is_nullable(layout) {
env.builder.build_is_null(tag_ptr, "is_tag_null")
} else {
env.context.bool_type().const_int(false as _, false)
},
cont_block,
check_if_unique,
);
@ -1262,8 +1220,20 @@ pub(crate) fn build_exp_expr<'a, 'ctx>(
let not_unique_block = ctx.append_basic_block(parent, "else_decref");
let refcount_ptr =
PointerToRefcount::from_ptr_to_data(env, tag_pointer_clear_tag_id(env, tag_ptr));
// reset is only generated for union values
let union_layout = match layout_interner.get_repr(layout) {
LayoutRepr::Union(ul) => ul,
_ => unreachable!(),
};
let refcount_ptr = PointerToRefcount::from_ptr_to_data(
env,
if union_layout.stores_tag_id_in_pointer(env.target_info) {
tag_pointer_clear_tag_id(env, tag_ptr)
} else {
tag_ptr
},
);
let is_unique = match update_mode {
UpdateMode::InPlace => env.context.bool_type().const_int(1, false),
@ -1386,12 +1356,13 @@ pub(crate) fn build_exp_expr<'a, 'ctx>(
layout_interner.get_repr(layout),
);
lookup_at_index_ptr2(
lookup_at_index_ptr(
env,
layout_interner,
field_layouts,
*index as usize,
ptr,
None,
target_loaded_type,
)
}
@ -1411,7 +1382,7 @@ pub(crate) fn build_exp_expr<'a, 'ctx>(
field_layouts,
*index as usize,
argument.into_pointer_value(),
struct_type.into_struct_type(),
Some(struct_type.into_struct_type()),
target_loaded_type,
)
}
@ -1437,12 +1408,13 @@ pub(crate) fn build_exp_expr<'a, 'ctx>(
layout_interner.get_repr(layout),
);
lookup_at_index_ptr2(
lookup_at_index_ptr(
env,
layout_interner,
field_layouts,
*index as usize,
ptr,
None,
target_loaded_type,
)
}
@ -1470,13 +1442,116 @@ pub(crate) fn build_exp_expr<'a, 'ctx>(
// the tag id is not stored
*index as usize,
argument.into_pointer_value(),
struct_type.into_struct_type(),
Some(struct_type.into_struct_type()),
target_loaded_type,
)
}
}
}
UnionFieldPtrAtIndex {
tag_id,
structure,
index,
union_layout,
} => {
// cast the argument bytes into the desired shape for this tag
let argument = scope.load_symbol(structure);
let ret_repr = layout_interner.get_repr(layout);
let pointer_value = match union_layout {
UnionLayout::NonRecursive(_) => unreachable!(),
UnionLayout::Recursive(tag_layouts) => {
debug_assert!(argument.is_pointer_value());
let field_layouts = tag_layouts[*tag_id as usize];
let ptr = tag_pointer_clear_tag_id(env, argument.into_pointer_value());
let target_loaded_type = basic_type_from_layout(env, layout_interner, ret_repr);
union_field_ptr_at_index(
env,
layout_interner,
field_layouts,
None,
*index as usize,
ptr,
target_loaded_type,
)
}
UnionLayout::NonNullableUnwrapped(field_layouts) => {
let struct_layout = LayoutRepr::struct_(field_layouts);
let struct_type = basic_type_from_layout(env, layout_interner, struct_layout);
let target_loaded_type = basic_type_from_layout(env, layout_interner, ret_repr);
union_field_ptr_at_index(
env,
layout_interner,
field_layouts,
Some(struct_type.into_struct_type()),
*index as usize,
argument.into_pointer_value(),
target_loaded_type,
)
}
UnionLayout::NullableWrapped {
nullable_id,
other_tags,
} => {
debug_assert!(argument.is_pointer_value());
debug_assert_ne!(*tag_id, *nullable_id);
let tag_index = if *tag_id < *nullable_id {
*tag_id
} else {
tag_id - 1
};
let field_layouts = other_tags[tag_index as usize];
let ptr = tag_pointer_clear_tag_id(env, argument.into_pointer_value());
let target_loaded_type = basic_type_from_layout(env, layout_interner, ret_repr);
union_field_ptr_at_index(
env,
layout_interner,
field_layouts,
None,
*index as usize,
ptr,
target_loaded_type,
)
}
UnionLayout::NullableUnwrapped {
nullable_id,
other_fields,
} => {
debug_assert!(argument.is_pointer_value());
debug_assert_ne!(*tag_id != 0, *nullable_id);
let field_layouts = other_fields;
let struct_layout = LayoutRepr::struct_(field_layouts);
let struct_type = basic_type_from_layout(env, layout_interner, struct_layout);
let target_loaded_type = basic_type_from_layout(env, layout_interner, ret_repr);
union_field_ptr_at_index(
env,
layout_interner,
field_layouts,
Some(struct_type.into_struct_type()),
// the tag id is not stored
*index as usize,
argument.into_pointer_value(),
target_loaded_type,
)
}
};
pointer_value.into()
}
GetTagId {
structure,
union_layout,
@ -1823,15 +1898,26 @@ fn tag_pointer_set_tag_id<'ctx>(
// we only have 3 bits, so can encode only 0..7 (or on 32-bit targets, 2 bits to encode 0..3)
debug_assert!((tag_id as u32) < env.target_info.ptr_width() as u32);
let ptr_int = env.ptr_int();
let tag_id_intval = env.ptr_int().const_int(tag_id as u64, false);
let as_int = env.builder.build_ptr_to_int(pointer, ptr_int, "to_int");
let cast_pointer = env.builder.build_pointer_cast(
pointer,
env.context.i8_type().ptr_type(AddressSpace::default()),
"cast_to_i8_ptr",
);
let tag_id_intval = ptr_int.const_int(tag_id as u64, false);
let combined = env.builder.build_or(as_int, tag_id_intval, "store_tag_id");
// NOTE: assumes the lower bits of `cast_pointer` are all 0
let indexed_pointer = unsafe {
env.builder.new_build_in_bounds_gep(
env.context.i8_type(),
cast_pointer,
&[tag_id_intval],
"indexed_pointer",
)
};
env.builder
.build_int_to_ptr(combined, pointer.get_type(), "to_ptr")
.build_pointer_cast(indexed_pointer, pointer.get_type(), "cast_from_i8_ptr")
}
pub fn tag_pointer_tag_id_bits_and_mask(target_info: TargetInfo) -> (u64, u64) {
@ -1861,22 +1947,35 @@ pub fn tag_pointer_clear_tag_id<'ctx>(
env: &Env<'_, 'ctx, '_>,
pointer: PointerValue<'ctx>,
) -> PointerValue<'ctx> {
let ptr_int = env.ptr_int();
let (_, tag_id_bits_mask) = tag_pointer_tag_id_bits_and_mask(env.target_info);
let (tag_id_bits_mask, _) = tag_pointer_tag_id_bits_and_mask(env.target_info);
let as_int = env
.builder
.build_ptr_to_int(pointer, env.ptr_int(), "to_int");
let as_int = env.builder.build_ptr_to_int(pointer, ptr_int, "to_int");
let mask = env.ptr_int().const_int(tag_id_bits_mask, false);
let mask = {
let a = env.ptr_int().const_all_ones();
let tag_id_bits = env.ptr_int().const_int(tag_id_bits_mask, false);
env.builder.build_left_shift(a, tag_id_bits, "make_mask")
let current_tag_id = env.builder.build_and(as_int, mask, "masked");
let index = env.builder.build_int_neg(current_tag_id, "index");
let cast_pointer = env.builder.build_pointer_cast(
pointer,
env.context.i8_type().ptr_type(AddressSpace::default()),
"cast_to_i8_ptr",
);
let indexed_pointer = unsafe {
env.builder.new_build_in_bounds_gep(
env.context.i8_type(),
cast_pointer,
&[index],
"new_ptr",
)
};
let masked = env.builder.build_and(as_int, mask, "masked");
env.builder
.build_int_to_ptr(masked, pointer.get_type(), "to_ptr")
.build_pointer_cast(indexed_pointer, pointer.get_type(), "cast_from_i8_ptr")
}
fn allocate_tag<'a, 'ctx>(
@ -1958,7 +2057,7 @@ pub fn get_tag_id<'a, 'ctx>(
match union_layout {
UnionLayout::NonRecursive(_) => {
debug_assert!(argument.is_pointer_value(), "{:?}", argument);
debug_assert!(argument.is_pointer_value(), "{argument:?}");
let argument_ptr = argument.into_pointer_value();
get_tag_id_wrapped(env, layout_interner, *union_layout, argument_ptr)
@ -2032,21 +2131,18 @@ fn lookup_at_index_ptr<'a, 'ctx>(
field_layouts: &[InLayout<'a>],
index: usize,
value: PointerValue<'ctx>,
struct_type: StructType<'ctx>,
struct_type: Option<StructType<'ctx>>,
target_loaded_type: BasicTypeEnum<'ctx>,
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
let ptr = env.builder.build_pointer_cast(
let elem_ptr = union_field_ptr_at_index_help(
env,
layout_interner,
field_layouts,
struct_type,
index,
value,
struct_type.ptr_type(AddressSpace::default()),
"cast_lookup_at_index_ptr",
);
let elem_ptr = builder
.new_build_struct_gep(struct_type, ptr, index as u32, "at_index_struct_gep")
.unwrap();
let field_layout = field_layouts[index];
let result = load_roc_value(
env,
@ -2061,19 +2157,23 @@ fn lookup_at_index_ptr<'a, 'ctx>(
cast_if_necessary_for_opaque_recursive_pointers(env.builder, result, target_loaded_type)
}
fn lookup_at_index_ptr2<'a, 'ctx>(
fn union_field_ptr_at_index_help<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>,
layout_interner: &STLayoutInterner<'a>,
field_layouts: &'a [InLayout<'a>],
opt_struct_type: Option<StructType<'ctx>>,
index: usize,
value: PointerValue<'ctx>,
target_loaded_type: BasicTypeEnum<'ctx>,
) -> BasicValueEnum<'ctx> {
) -> PointerValue<'ctx> {
let builder = env.builder;
let struct_layout = LayoutRepr::struct_(field_layouts);
let struct_type =
basic_type_from_layout(env, layout_interner, struct_layout).into_struct_type();
let struct_type = match opt_struct_type {
Some(st) => st,
None => {
let struct_layout = LayoutRepr::struct_(field_layouts);
basic_type_from_layout(env, layout_interner, struct_layout).into_struct_type()
}
};
let data_ptr = env.builder.build_pointer_cast(
value,
@ -2081,27 +2181,40 @@ fn lookup_at_index_ptr2<'a, 'ctx>(
"cast_lookup_at_index_ptr",
);
let elem_ptr = builder
builder
.new_build_struct_gep(
struct_type,
data_ptr,
index as u32,
"at_index_struct_gep_data",
)
.unwrap();
.unwrap()
}
let field_layout = field_layouts[index];
let result = load_roc_value(
fn union_field_ptr_at_index<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>,
layout_interner: &STLayoutInterner<'a>,
field_layouts: &'a [InLayout<'a>],
opt_struct_type: Option<StructType<'ctx>>,
index: usize,
value: PointerValue<'ctx>,
target_loaded_type: BasicTypeEnum<'ctx>,
) -> PointerValue<'ctx> {
let result = union_field_ptr_at_index_help(
env,
layout_interner,
layout_interner.get_repr(field_layout),
elem_ptr,
"load_at_index_ptr",
field_layouts,
opt_struct_type,
index,
value,
);
// A recursive pointer in the loaded structure is stored as a `i64*`, but the loaded layout
// might want a more precise structure. As such, cast it to the refined type if needed.
cast_if_necessary_for_opaque_recursive_pointers(env.builder, result, target_loaded_type)
let from_value: BasicValueEnum = result.into();
let to_type: BasicTypeEnum = target_loaded_type;
cast_if_necessary_for_opaque_recursive_pointers(env.builder, from_value, to_type)
.into_pointer_value()
}
pub fn reserve_with_refcount<'a, 'ctx>(
@ -2784,6 +2897,39 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>(
cont,
)
}
Free(symbol) => {
// unconditionally deallocate the symbol
let (value, layout) = scope.load_symbol_and_layout(symbol);
let alignment = layout_interner.alignment_bytes(layout);
debug_assert!(value.is_pointer_value());
let value = value.into_pointer_value();
let clear_tag_id = match layout_interner.chase_recursive(layout) {
LayoutRepr::Union(union) => union.stores_tag_id_in_pointer(env.target_info),
_ => false,
};
let ptr = if clear_tag_id {
tag_pointer_clear_tag_id(env, value)
} else {
value
};
let rc_ptr = PointerToRefcount::from_ptr_to_data(env, ptr);
rc_ptr.deallocate(env, alignment);
build_exp_stmt(
env,
layout_interner,
layout_ids,
func_spec_solutions,
scope,
parent,
cont,
)
}
}
}
@ -3078,7 +3224,7 @@ pub fn cast_if_necessary_for_opaque_recursive_pointers<'ctx>(
to_type: BasicTypeEnum<'ctx>,
) -> BasicValueEnum<'ctx> {
if from_value.get_type() != to_type
// Only perform the cast if the target types are transumatble.
// Only perform the cast if the target types are transmutable.
&& equivalent_type_constructors(&from_value.get_type(), &to_type)
{
complex_bitcast(
@ -3369,10 +3515,7 @@ fn build_switch_ir<'a, 'ctx>(
layout_interner,
layout_interner.get_repr(stored_layout)
),
"This switch matches on {:?}, but the matched-on symbol {:?} has layout {:?}",
cond_layout,
cond_symbol,
stored_layout
"This switch matches on {cond_layout:?}, but the matched-on symbol {cond_symbol:?} has layout {stored_layout:?}"
);
let cont_block = context.append_basic_block(parent, "cont");
@ -3478,7 +3621,7 @@ fn build_switch_ir<'a, 'ctx>(
condition_int_type.const_int(*int, false)
};
let block = context.append_basic_block(parent, format!("branch{}", int).as_str());
let block = context.append_basic_block(parent, format!("branch{int}").as_str());
cases.push((int_val, block));
}
@ -3819,11 +3962,9 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx>(
arguments_for_call.push(*arg);
} else {
match layout_interner.get_repr(*layout) {
LayoutRepr::Builtin(Builtin::List(_)) => {
let list_type = arg_type
.into_pointer_type()
.get_element_type()
.into_struct_type();
repr @ LayoutRepr::Builtin(Builtin::List(_)) => {
let list_type = basic_type_from_layout(env, layout_interner, repr);
let loaded = env.builder.new_build_load(
list_type,
arg.into_pointer_value(),
@ -3889,7 +4030,7 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx>(
&[],
);
let size_function_name: String = format!("roc__{}_size", ident_string);
let size_function_name: String = format!("roc__{ident_string}_size");
let size_function = add_func(
env.context,
@ -4189,7 +4330,7 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx>(
roc_function,
arguments,
return_layout,
&format!("{}_generic", c_function_name),
&format!("{c_function_name}_generic"),
);
let c_function = expose_function_to_host_help_c_abi_v2(
@ -4208,7 +4349,7 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx>(
Some(env.context.i64_type().as_basic_type_enum()),
&[],
);
let size_function_name: String = format!("{}_size", c_function_name);
let size_function_name: String = format!("{c_function_name}_size");
let size_function = add_func(
env.context,
@ -4855,7 +4996,7 @@ pub fn build_procedures_expose_expects<'a>(
let mut it = func_solutions.specs();
let func_spec = match it.next() {
Some(spec) => spec,
None => panic!("no specialization for expect {}", symbol),
None => panic!("no specialization for expect {symbol}"),
};
debug_assert!(
@ -4869,7 +5010,7 @@ pub fn build_procedures_expose_expects<'a>(
let name = roc_main_fn.get_name().to_str().unwrap();
let expect_name = &format!("Expect_{}", name);
let expect_name = &format!("Expect_{name}");
let expect_name = env.arena.alloc_str(expect_name);
expect_names.push(&*expect_name);
@ -4881,7 +5022,7 @@ pub fn build_procedures_expose_expects<'a>(
roc_main_fn,
top_level.arguments,
top_level.result,
&format!("Expect_{}", name),
&format!("Expect_{name}"),
);
}
@ -4908,7 +5049,7 @@ fn build_procedures_help<'a>(
entry_point,
it,
) {
Err(e) => panic!("Error in alias analysis: {}", e),
Err(e) => panic!("Error in alias analysis: {e}"),
Ok(solutions) => solutions,
};
@ -5004,10 +5145,10 @@ fn func_spec_name<'a>(
let ident_string = symbol.as_str(interns);
let module_string = interns.module_ids.get_name(symbol.module_id()).unwrap();
write!(buf, "{}_{}_", module_string, ident_string).unwrap();
write!(buf, "{module_string}_{ident_string}_").unwrap();
for byte in func_spec.0.iter() {
write!(buf, "{:x?}", byte).unwrap();
write!(buf, "{byte:x?}").unwrap();
}
buf
@ -5072,14 +5213,14 @@ fn build_proc_header<'a, 'ctx>(
if false {
let kind_id = Attribute::get_named_enum_kind_id("alwaysinline");
debug_assert!(kind_id > 0);
let enum_attr = env.context.create_enum_attribute(kind_id, 1);
let enum_attr = env.context.create_enum_attribute(kind_id, 0);
fn_val.add_attribute(AttributeLoc::Function, enum_attr);
}
if false {
let kind_id = Attribute::get_named_enum_kind_id("noinline");
debug_assert!(kind_id > 0);
let enum_attr = env.context.create_enum_attribute(kind_id, 1);
let enum_attr = env.context.create_enum_attribute(kind_id, 0);
fn_val.add_attribute(AttributeLoc::Function, enum_attr);
}
@ -5212,7 +5353,7 @@ fn build_closure_caller<'a, 'ctx>(
// STEP 1: build function header
// e.g. `roc__mainForHost_0_caller` (def_name is `mainForHost_0`)
let function_name = format!("roc__{}_caller", def_name);
let function_name = format!("roc__{def_name}_caller");
let function_spec = FunctionSpec::cconv(env, CCReturn::Void, None, &argument_types);
@ -5324,9 +5465,9 @@ fn build_host_exposed_alias_size_help<'a, 'ctx>(
let i64 = env.context.i64_type().as_basic_type_enum();
let size_function_spec = FunctionSpec::cconv(env, CCReturn::Return, Some(i64), &[]);
let size_function_name: String = if let Some(label) = opt_label {
format!("roc__{}_{}_size", def_name, label)
format!("roc__{def_name}_{label}_size")
} else {
format!("roc__{}_size", def_name,)
format!("roc__{def_name}_size",)
};
let size_function = add_func(
@ -5461,10 +5602,7 @@ fn function_value_by_name_help<'a, 'ctx>(
);
eprintln!("Is the function defined? If so, maybe there is a problem with the layout");
panic!(
"Unrecognized builtin function: {:?} (symbol: {:?})",
fn_name, symbol,
)
panic!("Unrecognized builtin function: {fn_name:?} (symbol: {symbol:?})",)
} else {
// Unrecognized non-builtin function:
eprintln!(
@ -5475,10 +5613,7 @@ fn function_value_by_name_help<'a, 'ctx>(
);
eprintln!("Is the function defined? If so, maybe there is a problem with the layout");
panic!(
"Unrecognized non-builtin function: {:?} (symbol: {:?})",
fn_name, symbol,
)
panic!("Unrecognized non-builtin function: {fn_name:?} (symbol: {symbol:?})",)
}
})
}
@ -6139,7 +6274,7 @@ fn define_global_str_literal<'ctx>(
message.hash(&mut hasher);
let hash = hasher.finish();
format!("_str_literal_{}", hash)
format!("_str_literal_{hash}")
};
match module.get_global(&name) {
@ -6240,7 +6375,7 @@ pub fn add_func<'ctx>(
) -> FunctionValue<'ctx> {
if cfg!(debug_assertions) {
if let Some(func) = module.get_function(name) {
panic!("Attempting to redefine LLVM function {}, which was already defined in this module as:\n\n{:#?}", name, func);
panic!("Attempting to redefine LLVM function {name}, which was already defined in this module as:\n\n{func:#?}");
}
}

View file

@ -144,14 +144,8 @@ fn build_eq<'a, 'ctx>(
lhs_val: BasicValueEnum<'ctx>,
rhs_val: BasicValueEnum<'ctx>,
lhs_layout: LayoutRepr<'a>,
rhs_layout: LayoutRepr<'a>,
_rhs_layout: LayoutRepr<'a>,
) -> BasicValueEnum<'ctx> {
debug_assert_eq!(
lhs_layout, rhs_layout,
"Equality of different layouts; did you have a type mismatch?\n{:?} == {:?}",
lhs_layout, rhs_layout
);
match lhs_layout {
LayoutRepr::Builtin(builtin) => build_eq_builtin(
env,
@ -185,7 +179,7 @@ fn build_eq<'a, 'ctx>(
rhs_val,
),
LayoutRepr::Boxed(inner_layout) => build_box_eq(
LayoutRepr::Ptr(inner_layout) => build_box_eq(
env,
layout_interner,
layout_ids,
@ -335,8 +329,7 @@ fn build_neq<'a, 'ctx>(
) -> BasicValueEnum<'ctx> {
if lhs_layout != rhs_layout {
panic!(
"Inequality of different layouts; did you have a type mismatch?\n{:?} != {:?}",
lhs_layout, rhs_layout
"Inequality of different layouts; did you have a type mismatch?\n{lhs_layout:?} != {rhs_layout:?}"
);
}
@ -385,7 +378,7 @@ fn build_neq<'a, 'ctx>(
result.into()
}
LayoutRepr::Boxed(inner_layout) => {
LayoutRepr::Ptr(inner_layout) => {
let is_equal = build_box_eq(
env,
layout_interner,
@ -795,7 +788,7 @@ fn build_struct_eq_help<'a, 'ctx>(
.into_int_value()
};
current = ctx.append_basic_block(parent, &format!("eq_step_{}", index));
current = ctx.append_basic_block(parent, &format!("eq_step_{index}"));
env.builder
.build_conditional_branch(are_equal, current, return_false);

View file

@ -14,8 +14,8 @@ use roc_target::TargetInfo;
use super::struct_::RocStruct;
pub fn basic_type_from_layout<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
pub fn basic_type_from_layout<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>,
layout_interner: &STLayoutInterner<'a>,
layout: LayoutRepr<'_>,
) -> BasicTypeEnum<'ctx> {
@ -30,7 +30,8 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>(
layout_interner,
layout_interner.get_repr(lambda_set.runtime_representation()),
),
Boxed(inner_layout) => {
Ptr(inner_layout) => {
let inner_type = basic_type_from_layout(
env,
layout_interner,
@ -40,6 +41,7 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>(
inner_type.ptr_type(AddressSpace::default()).into()
}
Union(union_layout) => basic_type_from_union_layout(env, layout_interner, &union_layout),
RecursivePointer(_) => env
.context
.i64_type()

View file

@ -18,7 +18,7 @@ use roc_mono::layout::{
use roc_region::all::Region;
use super::build::BuilderExt;
use super::build::{add_func, load_roc_value, FunctionSpec, LlvmBackendMode};
use super::build::{add_func, FunctionSpec, LlvmBackendMode};
use super::convert::struct_type_from_union_layout;
use super::scope::Scope;
use super::struct_::RocStruct;
@ -353,41 +353,8 @@ fn build_clone<'a, 'ctx>(
}
}
LayoutRepr::Boxed(inner_layout) => {
// write the offset
build_copy(env, ptr, cursors.offset, cursors.extra_offset.into());
let source = value.into_pointer_value();
let value = load_roc_value(
env,
layout_interner,
layout_interner.get_repr(inner_layout),
source,
"inner",
);
let inner_width = env
.ptr_int()
.const_int(layout_interner.stack_size(inner_layout) as u64, false);
let new_extra = env
.builder
.build_int_add(cursors.offset, inner_width, "new_extra");
let cursors = Cursors {
offset: cursors.extra_offset,
extra_offset: new_extra,
};
build_clone(
env,
layout_interner,
layout_ids,
ptr,
cursors,
value,
layout_interner.get_repr(inner_layout),
)
LayoutRepr::Ptr(_) => {
unreachable!("for internal use only")
}
LayoutRepr::RecursivePointer(rec_layout) => {

View file

@ -1,4 +1,6 @@
use inkwell::{
attributes::{Attribute, AttributeLoc},
module::Linkage,
types::{BasicType, IntType},
values::{
BasicValue, BasicValueEnum, FloatValue, FunctionValue, InstructionOpcode, IntValue,
@ -28,8 +30,8 @@ use crate::llvm::{
},
build::{
cast_basic_basic, complex_bitcast_check_size, create_entry_block_alloca,
function_value_by_func_spec, load_roc_value, roc_function_call, tag_pointer_clear_tag_id,
BuilderExt, RocReturn,
entry_block_alloca_zerofill, function_value_by_func_spec, load_roc_value,
roc_function_call, tag_pointer_clear_tag_id, BuilderExt, RocReturn,
},
build_list::{
list_append_unsafe, list_concat, list_drop_at, list_get_unsafe, list_len, list_map,
@ -56,7 +58,7 @@ use crate::llvm::{
use super::{build::Env, convert::zig_dec_type};
use super::{
build::{throw_internal_exception, use_roc_value},
build::{throw_internal_exception, use_roc_value, FAST_CALL_CONV},
convert::zig_with_overflow_roc_dec,
scope::Scope,
};
@ -1304,8 +1306,42 @@ pub(crate) fn run_low_level<'a, 'ctx>(
.into()
}
PtrWrite | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr
| RefCountDecDataPtr => {
PtrStore => {
arguments!(ptr, value);
env.builder.build_store(ptr.into_pointer_value(), value);
// ptr
env.context.struct_type(&[], false).const_zero().into()
}
PtrLoad => {
arguments!(ptr);
let ret_repr = layout_interner.get_repr(layout);
let element_type = basic_type_from_layout(env, layout_interner, ret_repr);
env.builder
.new_build_load(element_type, ptr.into_pointer_value(), "ptr_load")
}
PtrClearTagId => {
arguments!(ptr);
tag_pointer_clear_tag_id(env, ptr.into_pointer_value()).into()
}
Alloca => {
arguments!(initial_value);
let ptr = entry_block_alloca_zerofill(env, initial_value.get_type(), "stack_value");
env.builder.build_store(ptr, initial_value);
ptr.into()
}
RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr | RefCountDecDataPtr => {
unreachable!("Not used in LLVM backend: {:?}", op);
}
@ -1788,7 +1824,7 @@ fn throw_on_overflow<'ctx>(
bd.position_at_end(throw_block);
throw_internal_exception(env, parent, message);
throw_because_overflow(env, message);
bd.position_at_end(then_block);
@ -1796,6 +1832,60 @@ fn throw_on_overflow<'ctx>(
.unwrap()
}
fn throw_because_overflow(env: &Env<'_, '_, '_>, message: &str) {
let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap();
let function_name = "throw_on_overflow";
let function = match env.module.get_function(function_name) {
Some(function_value) => function_value,
None => {
let function_type = env.context.void_type().fn_type(&[], false);
let function_value =
env.module
.add_function(function_name, function_type, Some(Linkage::Internal));
function_value.set_call_conventions(FAST_CALL_CONV);
// prevent inlining of this function
let kind_id = Attribute::get_named_enum_kind_id("noinline");
debug_assert!(kind_id > 0);
let enum_attr = env.context.create_enum_attribute(kind_id, 0);
function_value.add_attribute(AttributeLoc::Function, enum_attr);
// calling this function is unlikely
let kind_id = Attribute::get_named_enum_kind_id("cold");
debug_assert!(kind_id > 0);
let enum_attr = env.context.create_enum_attribute(kind_id, 0);
function_value.add_attribute(AttributeLoc::Function, enum_attr);
// this function never returns
let kind_id = Attribute::get_named_enum_kind_id("noreturn");
debug_assert!(kind_id > 0);
let enum_attr = env.context.create_enum_attribute(kind_id, 0);
function_value.add_attribute(AttributeLoc::Function, enum_attr);
// Add a basic block for the entry point
let entry = env.context.append_basic_block(function_value, "entry");
env.builder.position_at_end(entry);
// ends in unreachable, so no return is needed
throw_internal_exception(env, function_value, message);
function_value
}
};
env.builder.position_at_end(block);
env.builder.set_current_debug_location(di_location);
let call = env.builder.build_call(function, &[], "overflow");
call.set_call_convention(FAST_CALL_CONV);
env.builder.build_unreachable();
}
fn dec_split_into_words<'ctx>(
env: &Env<'_, 'ctx, '_>,
value: IntValue<'ctx>,
@ -2802,6 +2892,6 @@ fn load_symbol_and_lambda_set<'a, 'ctx>(
let (ptr, layout) = scope.load_symbol_and_layout(symbol);
match layout_interner.get_repr(layout) {
LayoutRepr::LambdaSet(lambda_set) => (ptr, lambda_set),
other => panic!("Not a lambda set: {:?}, {:?}", other, ptr),
other => panic!("Not a lambda set: {other:?}, {ptr:?}"),
}
}

View file

@ -14,7 +14,7 @@ use bumpalo::collections::Vec;
use inkwell::basic_block::BasicBlock;
use inkwell::module::Linkage;
use inkwell::types::{AnyTypeEnum, BasicMetadataTypeEnum, BasicType, BasicTypeEnum};
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue};
use inkwell::values::{BasicValueEnum, FunctionValue, InstructionValue, IntValue, PointerValue};
use inkwell::{AddressSpace, IntPredicate};
use roc_module::symbol::Interns;
use roc_module::symbol::Symbol;
@ -132,7 +132,7 @@ impl<'ctx> PointerToRefcount<'ctx> {
let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap();
let fn_name = &format!("decrement_refcounted_ptr_{}", alignment);
let fn_name = &format!("decrement_refcounted_ptr_{alignment}");
let function = match env.module.get_function(fn_name) {
Some(function_value) => function_value,
@ -193,6 +193,14 @@ impl<'ctx> PointerToRefcount<'ctx> {
builder.build_return(None);
}
pub fn deallocate<'a, 'env>(
&self,
env: &Env<'a, 'ctx, 'env>,
alignment: u32,
) -> InstructionValue<'ctx> {
free_pointer(env, self.value, alignment)
}
}
fn incref_pointer<'ctx>(
@ -216,6 +224,28 @@ fn incref_pointer<'ctx>(
);
}
fn free_pointer<'ctx>(
env: &Env<'_, 'ctx, '_>,
pointer: PointerValue<'ctx>,
alignment: u32,
) -> InstructionValue<'ctx> {
let alignment = env.context.i32_type().const_int(alignment as _, false);
call_void_bitcode_fn(
env,
&[
env.builder
.build_pointer_cast(
pointer,
env.ptr_int().ptr_type(AddressSpace::default()),
"to_isize_ptr",
)
.into(),
alignment.into(),
],
roc_builtins::bitcode::UTILS_FREE_RC_PTR,
)
}
fn decref_pointer<'ctx>(env: &Env<'_, 'ctx, '_>, pointer: PointerValue<'ctx>, alignment: u32) {
let alignment = env.context.i32_type().const_int(alignment as _, false);
call_void_bitcode_fn(
@ -531,10 +561,10 @@ fn modify_refcount_layout_build_function<'a, 'ctx>(
modify_refcount_builtin(env, layout_interner, layout_ids, mode, layout, &builtin)
}
Boxed(inner) => {
let function = modify_refcount_boxed(env, layout_interner, layout_ids, mode, inner);
Ptr(_inner) => {
debug_assert_eq!(true, false);
Some(function)
None
}
Union(variant) => {
@ -861,130 +891,6 @@ fn modify_refcount_str_help<'a, 'ctx>(
builder.build_return(None);
}
fn modify_refcount_boxed<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>,
layout_interner: &STLayoutInterner<'a>,
layout_ids: &mut LayoutIds<'a>,
mode: Mode,
inner_layout: InLayout<'a>,
) -> FunctionValue<'ctx> {
let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap();
let boxed_layout = LayoutRepr::Boxed(inner_layout);
let (_, fn_name) = function_name_from_mode(
layout_ids,
&env.interns,
"increment_boxed",
"decrement_boxed",
boxed_layout,
mode,
);
let function = match env.module.get_function(fn_name.as_str()) {
Some(function_value) => function_value,
None => {
let basic_type = basic_type_from_layout(env, layout_interner, boxed_layout);
let function_value = build_header(env, basic_type, mode, &fn_name);
modify_refcount_box_help(
env,
layout_interner,
layout_ids,
mode,
inner_layout,
function_value,
);
function_value
}
};
env.builder.position_at_end(block);
env.builder.set_current_debug_location(di_location);
function
}
fn modify_refcount_box_help<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>,
layout_interner: &STLayoutInterner<'a>,
layout_ids: &mut LayoutIds<'a>,
mode: Mode,
inner_layout: InLayout<'a>,
fn_val: FunctionValue<'ctx>,
) {
let builder = env.builder;
let ctx = env.context;
// Add a basic block for the entry point
let entry = ctx.append_basic_block(fn_val, "entry");
builder.position_at_end(entry);
debug_info_init!(env, fn_val);
// Add args to scope
let arg_symbol = Symbol::ARG_1;
let arg_val = fn_val.get_param_iter().next().unwrap();
arg_val.set_name(arg_symbol.as_str(&env.interns));
let boxed = arg_val.into_pointer_value();
let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, boxed);
let call_mode = mode_to_call_mode(fn_val, mode);
let boxed_layout = LayoutRepr::Boxed(inner_layout);
match mode {
Mode::Inc => {
refcount_ptr.modify(call_mode, boxed_layout, env, layout_interner);
builder.build_return(None);
}
Mode::Dec => {
// if the box is unique, also decrement its inner value
let do_recurse_block = env.context.append_basic_block(fn_val, "do_recurse");
let no_recurse_block = env.context.append_basic_block(fn_val, "no_recurse");
builder.build_conditional_branch(
refcount_ptr.is_1(env),
do_recurse_block,
no_recurse_block,
);
{
env.builder.position_at_end(do_recurse_block);
let inner = load_roc_value(
env,
layout_interner,
layout_interner.get_repr(inner_layout),
boxed,
"inner",
);
modify_refcount_layout(
env,
layout_interner,
layout_ids,
call_mode,
inner,
inner_layout,
);
refcount_ptr.modify(call_mode, boxed_layout, env, layout_interner);
env.builder.build_return(None);
}
{
env.builder.position_at_end(no_recurse_block);
refcount_ptr.modify(call_mode, boxed_layout, env, layout_interner);
env.builder.build_return(None);
}
}
}
}
/// Build an increment or decrement function for a specific layout
fn build_header<'ctx>(
env: &Env<'_, 'ctx, '_>,
@ -1492,7 +1398,7 @@ pub fn build_reset<'a, 'ctx>(
let union_layout_repr = LayoutRepr::Union(union_layout);
let layout_id = layout_ids.get(Symbol::DEC, &union_layout_repr);
let fn_name = layout_id.to_symbol_string(Symbol::DEC, &env.interns);
let fn_name = format!("{}_reset", fn_name);
let fn_name = format!("{fn_name}_reset");
let dec_function = build_rec_union(env, layout_interner, layout_ids, Mode::Dec, union_layout);

View file

@ -28,17 +28,14 @@ impl<'a, 'ctx> Scope<'a, 'ctx> {
match self.symbols.get(symbol) {
Some((_, ptr)) => *ptr,
None => panic!(
"There was no entry for {:?} {} in scope {:?}",
symbol, symbol, self
),
None => panic!("There was no entry for {symbol:?} {symbol} in scope {self:?}"),
}
}
pub fn load_symbol_and_layout(&self, symbol: &Symbol) -> (BasicValueEnum<'ctx>, InLayout<'a>) {
match self.symbols.get(symbol) {
Some((layout, ptr)) => (*ptr, *layout),
None => panic!("There was no entry for {:?} in scope {:?}", symbol, self),
None => panic!("There was no entry for {symbol:?} in scope {self:?}"),
}
}

View file

@ -92,7 +92,15 @@ impl<'ctx> RocStruct<'ctx> {
index_struct_value(env, layout_interner, field_layouts, *argument, index)
}
(Self::ByReference(ptr), LayoutRepr::Struct(field_layouts)) => {
index_struct_ptr(env, layout_interner, field_layouts, *ptr, index)
let struct_type = basic_type_from_layout(env, layout_interner, struct_layout);
index_struct_ptr(
env,
layout_interner,
struct_type.into_struct_type(),
field_layouts,
*ptr,
index,
)
}
(other, layout) => {
unreachable!(
@ -118,7 +126,7 @@ fn index_struct_value<'a, 'ctx>(
argument,
index as _,
env.arena
.alloc(format!("struct_field_access_record_{}", index)),
.alloc(format!("struct_field_access_record_{index}")),
);
let field_layout = field_layouts[index as usize];
@ -135,26 +143,26 @@ fn index_struct_value<'a, 'ctx>(
fn index_struct_ptr<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>,
layout_interner: &STLayoutInterner<'a>,
struct_type: StructType<'ctx>,
field_layouts: &[InLayout<'a>],
ptr: PointerValue<'ctx>,
index: u64,
) -> BasicValueEnum<'ctx> {
debug_assert!(!field_layouts.is_empty());
let field_value = get_field_from_ptr(
env,
ptr,
index as _,
env.arena
.alloc(format!("struct_field_access_record_{}", index)),
);
let field_layout = field_layouts[index as usize];
let field_repr = layout_interner.get_repr(field_layout);
let name = format!("struct_field_access_record_{index}");
let field_value = env
.builder
.new_build_struct_gep(struct_type, ptr, index as u32, &name)
.unwrap();
load_roc_value(
env,
layout_interner,
layout_interner.get_repr(field_layout),
field_repr,
field_value,
"struct_field",
)
@ -171,15 +179,6 @@ fn get_field_from_value<'ctx>(
.unwrap()
}
fn get_field_from_ptr<'ctx>(
env: &Env<'_, 'ctx, '_>,
ptr: PointerValue<'ctx>,
index: u32,
name: &str,
) -> PointerValue<'ctx> {
env.builder.build_struct_gep(ptr, index, name).unwrap()
}
struct BuildStruct<'ctx> {
struct_type: StructType<'ctx>,
struct_val: StructValue<'ctx>,