mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 06:14:46 +00:00
Merge remote-tracking branch 'origin/main' into linux64
This commit is contained in:
commit
1d5c5b25ec
328 changed files with 11845 additions and 9244 deletions
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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:#?}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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:?}"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue