Merge remote-tracking branch 'remote/main' into upgrade-llvm-zig

This commit is contained in:
Luke Boswell 2024-10-08 18:21:24 +11:00
commit 915097d792
No known key found for this signature in database
GPG key ID: F6DB3C9DB47377B0
129 changed files with 3396 additions and 4634 deletions

View file

@ -2828,130 +2828,108 @@ fn list_literal<'a, 'ctx>(
let list_length = elems.len();
let list_length_intval = env.ptr_int().const_int(list_length as _, false);
// TODO re-enable, currently causes morphic segfaults because it tries to update
// constants in-place...
// if element_type.is_int_type() {
if false {
let element_type = element_type.into_int_type();
let is_refcounted = layout_interner.contains_refcounted(element_layout);
let is_all_constant = elems.iter().all(|e| e.is_literal());
if is_all_constant && !is_refcounted {
// Build a global literal in-place instead of GEPing and storing individual elements.
// Exceptions:
// - Anything that is refcounted has nested pointers,
// and nested pointers in globals will break the surgical linker.
// Ignore such cases for now.
let element_width = layout_interner.stack_size(element_layout);
let size = list_length * element_width as usize;
let alignment = layout_interner
.alignment_bytes(element_layout)
.max(env.target.ptr_width() as u32);
let mut is_all_constant = true;
let zero_elements =
(env.target.ptr_width() as u8 as f64 / element_width as f64).ceil() as usize;
let refcount_slot_bytes = alignment as usize;
// runtime-evaluated elements
let mut runtime_evaluated_elements = Vec::with_capacity_in(list_length, env.arena);
let refcount_slot_elements =
(refcount_slot_bytes as f64 / element_width as f64).ceil() as usize;
// set up a global that contains all the literal elements of the array
// any variables or expressions are represented as `undef`
let global = {
let mut global_elements = Vec::with_capacity_in(list_length, env.arena);
let data_bytes = list_length * element_width as usize;
// Add zero bytes that represent the refcount
//
// - if all elements are const, then we store the whole list as a constant.
// It then needs a refcount before the first element.
// - but if the list is not all constants, then we will just copy the constant values,
// and we do not need that refcount at the start
//
// In the latter case, we won't store the zeros in the globals
// (we slice them off again below)
for _ in 0..zero_elements {
global_elements.push(element_type.const_zero());
assert!(refcount_slot_elements > 0);
let global = if element_type.is_int_type() {
let element_type = element_type.into_int_type();
let mut bytes = Vec::with_capacity_in(refcount_slot_elements + data_bytes, env.arena);
// Fill the refcount slot with nulls
for _ in 0..(refcount_slot_elements) {
bytes.push(element_type.const_zero());
}
// Copy the elements from the list literal into the array
for (index, element) in elems.iter().enumerate() {
match element {
ListLiteralElement::Literal(literal) => {
let val = build_exp_literal(env, layout_interner, element_layout, literal);
global_elements.push(val.into_int_value());
}
ListLiteralElement::Symbol(symbol) => {
let val = scope.load_symbol(symbol);
// here we'd like to furthermore check for intval.is_const().
// if all elements are const for LLVM, we could make the array a constant.
// BUT morphic does not know about this, and could allow us to modify that
// array in-place. That would cause a segfault. So, we'll have to find
// constants ourselves and cannot lean on LLVM here.
is_all_constant = false;
runtime_evaluated_elements.push((index, val));
global_elements.push(element_type.get_undef());
}
};
for element in elems.iter() {
let literal = element.get_literal().expect("is_all_constant is true");
let val = build_exp_literal(env, layout_interner, element_layout, &literal);
bytes.push(val.into_int_value());
}
let const_elements = if is_all_constant {
global_elements.into_bump_slice()
} else {
&global_elements[zero_elements..]
};
// use None for the address space (e.g. Const does not work)
let typ = element_type.array_type(const_elements.len() as u32);
let typ = element_type.array_type(bytes.len() as u32);
let global = env.module.add_global(typ, None, "roc__list_literal");
global.set_constant(true);
global.set_alignment(alignment);
global.set_unnamed_addr(true);
global.set_linkage(inkwell::module::Linkage::Private);
global.set_initializer(&element_type.const_array(bytes.into_bump_slice()));
global
} else if element_type.is_float_type() {
let element_type = element_type.into_float_type();
let mut bytes = Vec::with_capacity_in(refcount_slot_elements + data_bytes, env.arena);
global.set_initializer(&element_type.const_array(const_elements));
global.as_pointer_value()
};
if is_all_constant {
// all elements are constants, so we can use the memory in the constants section directly
// here we make a pointer to the first actual element (skipping the 0 bytes that
// represent the refcount)
let zero = env.ptr_int().const_zero();
let offset = env.ptr_int().const_int(zero_elements as _, false);
let ptr = unsafe {
env.builder.new_build_in_bounds_gep(
element_type,
global,
&[zero, offset],
"first_element_pointer",
)
};
super::build_list::store_list(env, ptr, list_length_intval).into()
} else {
// some of our elements are non-constant, so we must allocate space on the heap
let ptr = allocate_list(env, layout_interner, element_layout, list_length_intval);
// then, copy the relevant segment from the constant section into the heap
env.builder
.build_memcpy(
ptr,
alignment,
global,
alignment,
env.ptr_int().const_int(size as _, false),
)
.unwrap();
// then replace the `undef`s with the values that we evaluate at runtime
for (index, val) in runtime_evaluated_elements {
let index_val = ctx.i64_type().const_int(index as u64, false);
let elem_ptr = unsafe {
builder.new_build_in_bounds_gep(element_type, ptr, &[index_val], "index")
};
builder.new_build_store(elem_ptr, val);
// Fill the refcount slot with nulls
for _ in 0..(refcount_slot_elements) {
bytes.push(element_type.const_zero());
}
super::build_list::store_list(env, ptr, list_length_intval).into()
}
// Copy the elements from the list literal into the array
for element in elems.iter() {
let literal = element.get_literal().expect("is_all_constant is true");
let val = build_exp_literal(env, layout_interner, element_layout, &literal);
bytes.push(val.into_float_value());
}
let typ = element_type.array_type(bytes.len() as u32);
let global = env.module.add_global(typ, None, "roc__list_literal");
global.set_initializer(&element_type.const_array(bytes.into_bump_slice()));
global
} else {
internal_error!("unexpected element type: {:?}", element_type);
};
global.set_constant(true);
global.set_alignment(alignment);
global.set_unnamed_addr(true);
global.set_linkage(inkwell::module::Linkage::Private);
// Refcount the global itself in a new allocation.
// Necessary because morphic MAY attempt to update the global in-place, which is
// illegal if the global is in the read-only section.
let with_rc_ptr = global.as_pointer_value();
let const_data_ptr = unsafe {
env.builder.new_build_in_bounds_gep(
element_type,
with_rc_ptr,
&[env
.ptr_int()
.const_int(refcount_slot_elements as u64, false)],
"get_data_ptr",
)
};
let data_ptr = allocate_list(env, layout_interner, element_layout, list_length_intval);
let byte_size = env
.ptr_int()
.const_int(list_length as u64 * element_width as u64, false);
builder
.build_memcpy(data_ptr, alignment, const_data_ptr, alignment, byte_size)
.unwrap();
super::build_list::store_list(env, data_ptr, list_length_intval).into()
} else {
let ptr = allocate_list(env, layout_interner, element_layout, list_length_intval);

View file

@ -263,16 +263,9 @@ pub(crate) struct RocUnion<'ctx> {
tag_type: Option<TagType>,
}
fn is_multiple_of(big: u32, small: u32) -> bool {
match small {
0 => true, // 0 is a multiple of all n, because n * 0 = 0
n => big % n == 0,
}
}
impl<'ctx> RocUnion<'ctx> {
pub const TAG_ID_INDEX: u32 = 2;
pub const TAG_DATA_INDEX: u32 = 1;
pub const TAG_ID_INDEX: u32 = 1;
pub const TAG_DATA_INDEX: u32 = 0;
fn new(
context: &'ctx Context,
@ -282,45 +275,24 @@ impl<'ctx> RocUnion<'ctx> {
) -> Self {
let bytes = round_up_to_alignment(data_width, data_align);
let byte_array_type = if is_multiple_of(bytes, 8) && is_multiple_of(data_align, 8) {
context
.i64_type()
.array_type(bytes / 8)
.as_basic_type_enum()
} else {
context.i8_type().array_type(bytes).as_basic_type_enum()
};
let alignment_array_type = alignment_type(context, data_align)
.array_type(0)
let align_type = alignment_type(context, data_align);
let byte_array_type = align_type
.array_type(bytes / data_align)
.as_basic_type_enum();
let struct_type = if let Some(tag_type) = tag_type {
let tag_width = match tag_type {
TagType::I8 => 1,
TagType::I16 => 2,
};
let tag_padding = round_up_to_alignment(tag_width, data_align) - tag_width;
let tag_padding_type = context
.i8_type()
.array_type(tag_padding)
.as_basic_type_enum();
context.struct_type(
&[
alignment_array_type,
byte_array_type,
match tag_type {
TagType::I8 => context.i8_type().into(),
TagType::I16 => context.i16_type().into(),
},
tag_padding_type,
],
false,
)
} else {
context.struct_type(&[alignment_array_type, byte_array_type], false)
context.struct_type(&[byte_array_type], false)
};
Self {