mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 21:39:07 +00:00
commit
eb04d4b9f6
14 changed files with 539 additions and 451 deletions
|
@ -2,9 +2,9 @@
|
|||
use crate::debug_info_init;
|
||||
use crate::llvm::build::{
|
||||
complex_bitcast_check_size, load_roc_value, struct_from_fields, to_cc_return, CCReturn, Env,
|
||||
C_CALL_CONV, FAST_CALL_CONV, TAG_DATA_INDEX,
|
||||
C_CALL_CONV, FAST_CALL_CONV,
|
||||
};
|
||||
use crate::llvm::convert::basic_type_from_layout;
|
||||
use crate::llvm::convert::{basic_type_from_layout, RocUnion};
|
||||
use crate::llvm::refcounting::{
|
||||
decrement_refcount_layout, increment_n_refcount_layout, increment_refcount_layout,
|
||||
};
|
||||
|
@ -181,7 +181,15 @@ pub fn call_bitcode_fn_fixing_for_convention<'a, 'ctx, 'env>(
|
|||
.try_into()
|
||||
.expect("Zig bitcode return type is not a basic type!");
|
||||
|
||||
// when we write an i128 into this (happens in NumToInt), zig expects this pointer to
|
||||
// be 16-byte aligned. Not doing so is UB and will immediately fail on CI
|
||||
let cc_return_value_ptr = env.builder.build_alloca(cc_return_type, "return_value");
|
||||
cc_return_value_ptr
|
||||
.as_instruction()
|
||||
.unwrap()
|
||||
.set_alignment(16)
|
||||
.unwrap();
|
||||
|
||||
let fixed_args: Vec<BasicValueEnum<'ctx>> = [cc_return_value_ptr.into()]
|
||||
.iter()
|
||||
.chain(args)
|
||||
|
@ -314,7 +322,7 @@ fn build_has_tag_id_help<'a, 'ctx, 'env>(
|
|||
let tag_data_ptr = {
|
||||
let ptr = env
|
||||
.builder
|
||||
.build_struct_gep(tag_value, TAG_DATA_INDEX, "get_data_ptr")
|
||||
.build_struct_gep(tag_value, RocUnion::TAG_DATA_INDEX, "get_data_ptr")
|
||||
.unwrap();
|
||||
|
||||
env.builder.build_bitcast(ptr, i8_ptr_type, "to_opaque")
|
||||
|
|
|
@ -11,8 +11,7 @@ use crate::llvm::build_list::{
|
|||
use crate::llvm::build_str::{dec_to_str, str_from_float, str_from_int};
|
||||
use crate::llvm::compare::{generic_eq, generic_neq};
|
||||
use crate::llvm::convert::{
|
||||
self, argument_type_from_layout, basic_type_from_builtin, basic_type_from_layout,
|
||||
block_of_memory_slices, zig_str_type,
|
||||
self, argument_type_from_layout, basic_type_from_builtin, basic_type_from_layout, zig_str_type,
|
||||
};
|
||||
use crate::llvm::refcounting::{
|
||||
build_reset, decrement_refcount_layout, increment_refcount_layout, PointerToRefcount,
|
||||
|
@ -65,7 +64,7 @@ use std::convert::TryInto;
|
|||
use std::path::Path;
|
||||
use target_lexicon::{Architecture, OperatingSystem, Triple};
|
||||
|
||||
use super::convert::zig_with_overflow_roc_dec;
|
||||
use super::convert::{zig_with_overflow_roc_dec, RocUnion};
|
||||
|
||||
#[inline(always)]
|
||||
fn print_fn_verification_output() -> bool {
|
||||
|
@ -1162,9 +1161,6 @@ pub fn build_exp_call<'a, 'ctx, 'env>(
|
|||
}
|
||||
}
|
||||
|
||||
pub const TAG_ID_INDEX: u32 = 1;
|
||||
pub const TAG_DATA_INDEX: u32 = 0;
|
||||
|
||||
pub fn struct_from_fields<'a, 'ctx, 'env, I>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
struct_type: StructType<'ctx>,
|
||||
|
@ -1240,40 +1236,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
call,
|
||||
),
|
||||
|
||||
Struct(sorted_fields) => {
|
||||
let ctx = env.context;
|
||||
|
||||
// Determine types
|
||||
let num_fields = sorted_fields.len();
|
||||
let mut field_types = Vec::with_capacity_in(num_fields, env.arena);
|
||||
let mut field_vals = Vec::with_capacity_in(num_fields, env.arena);
|
||||
|
||||
for symbol in sorted_fields.iter() {
|
||||
// Zero-sized fields have no runtime representation.
|
||||
// The layout of the struct expects them to be dropped!
|
||||
let (field_expr, field_layout) = load_symbol_and_layout(scope, symbol);
|
||||
if !field_layout.is_dropped_because_empty() {
|
||||
field_types.push(basic_type_from_layout(env, field_layout));
|
||||
|
||||
if field_layout.is_passed_by_reference(env.target_info) {
|
||||
let field_value = env.builder.build_load(
|
||||
field_expr.into_pointer_value(),
|
||||
"load_tag_to_put_in_struct",
|
||||
);
|
||||
|
||||
field_vals.push(field_value);
|
||||
} else {
|
||||
field_vals.push(field_expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the struct_type
|
||||
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
||||
|
||||
// Insert field exprs into struct_val
|
||||
struct_from_fields(env, struct_type, field_vals.into_iter().enumerate()).into()
|
||||
}
|
||||
Struct(sorted_fields) => build_struct(env, scope, sorted_fields).into(),
|
||||
|
||||
Reuse {
|
||||
arguments,
|
||||
|
@ -1465,16 +1428,34 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
|
||||
let field_layouts = tag_layouts[*tag_id as usize];
|
||||
|
||||
let tag_id_type =
|
||||
basic_type_from_layout(env, &union_layout.tag_id_layout()).into_int_type();
|
||||
let struct_layout = Layout::struct_no_name_order(field_layouts);
|
||||
let struct_type = basic_type_from_layout(env, &struct_layout);
|
||||
|
||||
lookup_at_index_ptr2(
|
||||
let opaque_data_ptr = env
|
||||
.builder
|
||||
.build_struct_gep(
|
||||
argument.into_pointer_value(),
|
||||
RocUnion::TAG_DATA_INDEX,
|
||||
"get_opaque_data_ptr",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let data_ptr = env.builder.build_pointer_cast(
|
||||
opaque_data_ptr,
|
||||
struct_type.ptr_type(AddressSpace::Generic),
|
||||
"to_data_pointer",
|
||||
);
|
||||
|
||||
let element_ptr = env
|
||||
.builder
|
||||
.build_struct_gep(data_ptr, *index as _, "get_opaque_data_ptr")
|
||||
.unwrap();
|
||||
|
||||
load_roc_value(
|
||||
env,
|
||||
union_layout,
|
||||
tag_id_type,
|
||||
field_layouts,
|
||||
*index as usize,
|
||||
argument.into_pointer_value(),
|
||||
field_layouts[*index as usize],
|
||||
element_ptr,
|
||||
"load_element",
|
||||
)
|
||||
}
|
||||
UnionLayout::Recursive(tag_layouts) => {
|
||||
|
@ -1482,19 +1463,9 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
|
||||
let field_layouts = tag_layouts[*tag_id as usize];
|
||||
|
||||
let tag_id_type =
|
||||
basic_type_from_layout(env, &union_layout.tag_id_layout()).into_int_type();
|
||||
|
||||
let ptr = tag_pointer_clear_tag_id(env, argument.into_pointer_value());
|
||||
|
||||
lookup_at_index_ptr2(
|
||||
env,
|
||||
union_layout,
|
||||
tag_id_type,
|
||||
field_layouts,
|
||||
*index as usize,
|
||||
ptr,
|
||||
)
|
||||
lookup_at_index_ptr2(env, union_layout, field_layouts, *index as usize, ptr)
|
||||
}
|
||||
UnionLayout::NonNullableUnwrapped(field_layouts) => {
|
||||
let struct_layout = Layout::struct_no_name_order(field_layouts);
|
||||
|
@ -1525,18 +1496,8 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
|
||||
let field_layouts = other_tags[tag_index as usize];
|
||||
|
||||
let tag_id_type =
|
||||
basic_type_from_layout(env, &union_layout.tag_id_layout()).into_int_type();
|
||||
|
||||
let ptr = tag_pointer_clear_tag_id(env, argument.into_pointer_value());
|
||||
lookup_at_index_ptr2(
|
||||
env,
|
||||
union_layout,
|
||||
tag_id_type,
|
||||
field_layouts,
|
||||
*index as usize,
|
||||
ptr,
|
||||
)
|
||||
lookup_at_index_ptr2(env, union_layout, field_layouts, *index as usize, ptr)
|
||||
}
|
||||
UnionLayout::NullableUnwrapped {
|
||||
nullable_id,
|
||||
|
@ -1599,7 +1560,7 @@ fn build_wrapped_tag<'a, 'ctx, 'env>(
|
|||
|
||||
if union_layout.stores_tag_id_as_data(env.target_info) {
|
||||
let tag_id_ptr = builder
|
||||
.build_struct_gep(raw_data_ptr, TAG_ID_INDEX, "tag_id_index")
|
||||
.build_struct_gep(raw_data_ptr, RocUnion::TAG_ID_INDEX, "tag_id_index")
|
||||
.unwrap();
|
||||
|
||||
let tag_id_type = basic_type_from_layout(env, &tag_id_layout).into_int_type();
|
||||
|
@ -1608,7 +1569,7 @@ fn build_wrapped_tag<'a, 'ctx, 'env>(
|
|||
.build_store(tag_id_ptr, tag_id_type.const_int(tag_id as u64, false));
|
||||
|
||||
let opaque_struct_ptr = builder
|
||||
.build_struct_gep(raw_data_ptr, TAG_DATA_INDEX, "tag_data_index")
|
||||
.build_struct_gep(raw_data_ptr, RocUnion::TAG_DATA_INDEX, "tag_data_index")
|
||||
.unwrap();
|
||||
|
||||
struct_pointer_from_fields(
|
||||
|
@ -1702,6 +1663,44 @@ fn build_tag_fields<'a, 'ctx, 'env>(
|
|||
(field_types, field_values)
|
||||
}
|
||||
|
||||
fn build_struct<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
sorted_fields: &[Symbol],
|
||||
) -> StructValue<'ctx> {
|
||||
let ctx = env.context;
|
||||
|
||||
// Determine types
|
||||
let num_fields = sorted_fields.len();
|
||||
let mut field_types = Vec::with_capacity_in(num_fields, env.arena);
|
||||
let mut field_vals = Vec::with_capacity_in(num_fields, env.arena);
|
||||
|
||||
for symbol in sorted_fields.iter() {
|
||||
// Zero-sized fields have no runtime representation.
|
||||
// The layout of the struct expects them to be dropped!
|
||||
let (field_expr, field_layout) = load_symbol_and_layout(scope, symbol);
|
||||
if !field_layout.is_dropped_because_empty() {
|
||||
field_types.push(basic_type_from_layout(env, field_layout));
|
||||
|
||||
if field_layout.is_passed_by_reference(env.target_info) {
|
||||
let field_value = env
|
||||
.builder
|
||||
.build_load(field_expr.into_pointer_value(), "load_tag_to_put_in_struct");
|
||||
|
||||
field_vals.push(field_value);
|
||||
} else {
|
||||
field_vals.push(field_expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the struct_type
|
||||
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
||||
|
||||
// Insert field exprs into struct_val
|
||||
struct_from_fields(env, struct_type, field_vals.into_iter().enumerate())
|
||||
}
|
||||
|
||||
fn build_tag<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
|
@ -1711,93 +1710,27 @@ fn build_tag<'a, 'ctx, 'env>(
|
|||
reuse_allocation: Option<PointerValue<'ctx>>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let tag_id_layout = union_layout.tag_id_layout();
|
||||
let union_size = union_layout.number_of_tags();
|
||||
|
||||
match union_layout {
|
||||
UnionLayout::NonRecursive(tags) => {
|
||||
debug_assert!(union_size > 1);
|
||||
|
||||
let internal_type = block_of_memory_slices(env.context, tags, env.target_info);
|
||||
let data = build_struct(env, scope, arguments);
|
||||
|
||||
let tag_id_type = basic_type_from_layout(env, &tag_id_layout).into_int_type();
|
||||
let wrapper_type = env
|
||||
.context
|
||||
.struct_type(&[internal_type, tag_id_type.into()], false);
|
||||
let result_alloca = entry_block_alloca_zerofill(env, wrapper_type.into(), "opaque_tag");
|
||||
let roc_union = RocUnion::tagged_from_slices(env.context, tags, env.target_info);
|
||||
let value = roc_union.as_struct_value(env, data, Some(tag_id as _));
|
||||
|
||||
// Determine types
|
||||
let num_fields = arguments.len() + 1;
|
||||
let mut field_types = Vec::with_capacity_in(num_fields, env.arena);
|
||||
let mut field_vals = Vec::with_capacity_in(num_fields, env.arena);
|
||||
|
||||
let tag_field_layouts = &tags[tag_id as usize];
|
||||
|
||||
for (field_symbol, tag_field_layout) in arguments.iter().zip(tag_field_layouts.iter()) {
|
||||
let (val, _val_layout) = load_symbol_and_layout(scope, field_symbol);
|
||||
|
||||
// Zero-sized fields have no runtime representation.
|
||||
// The layout of the struct expects them to be dropped!
|
||||
if !tag_field_layout.is_dropped_because_empty() {
|
||||
let field_type = basic_type_from_layout(env, tag_field_layout);
|
||||
|
||||
field_types.push(field_type);
|
||||
|
||||
if let Layout::RecursivePointer = tag_field_layout {
|
||||
panic!(
|
||||
r"non-recursive tag unions cannot directly contain a recursive pointer"
|
||||
);
|
||||
} else {
|
||||
// this check fails for recursive tag unions, but can be helpful while debugging
|
||||
// debug_assert_eq!(tag_field_layout, val_layout);
|
||||
|
||||
field_vals.push(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
// store the tag id
|
||||
let tag_id_ptr = env
|
||||
.builder
|
||||
.build_struct_gep(result_alloca, TAG_ID_INDEX, "tag_id_ptr")
|
||||
.unwrap();
|
||||
|
||||
let tag_id_intval = tag_id_type.const_int(tag_id as u64, false);
|
||||
env.builder.build_store(tag_id_ptr, tag_id_intval);
|
||||
|
||||
// Create the struct_type
|
||||
let struct_type = env
|
||||
.context
|
||||
.struct_type(field_types.into_bump_slice(), false);
|
||||
|
||||
let struct_opaque_ptr = env
|
||||
.builder
|
||||
.build_struct_gep(result_alloca, TAG_DATA_INDEX, "opaque_data_ptr")
|
||||
.unwrap();
|
||||
let struct_ptr = env.builder.build_pointer_cast(
|
||||
struct_opaque_ptr,
|
||||
struct_type.ptr_type(AddressSpace::Generic),
|
||||
"to_specific",
|
||||
let alloca = create_entry_block_alloca(
|
||||
env,
|
||||
parent,
|
||||
value.get_type().into(),
|
||||
"non_recursive_tag_alloca",
|
||||
);
|
||||
|
||||
// Insert field exprs into struct_val
|
||||
//let struct_val =
|
||||
//struct_from_fields(env, struct_type, field_vals.into_iter().enumerate());
|
||||
env.builder.build_store(alloca, value);
|
||||
|
||||
// Insert field exprs into struct_val
|
||||
for (index, field_val) in field_vals.iter().copied().enumerate() {
|
||||
let index: u32 = index as u32;
|
||||
|
||||
let ptr = env
|
||||
.builder
|
||||
.build_struct_gep(struct_ptr, index, "get_tag_field_ptr")
|
||||
.unwrap();
|
||||
|
||||
let field_layout = tag_field_layouts[index as usize];
|
||||
store_roc_value(env, field_layout, ptr, field_val);
|
||||
}
|
||||
|
||||
// env.builder.build_load(result_alloca, "load_result")
|
||||
result_alloca.into()
|
||||
alloca.into()
|
||||
}
|
||||
UnionLayout::Recursive(tags) => {
|
||||
debug_assert!(union_size > 1);
|
||||
|
@ -1876,11 +1809,11 @@ fn build_tag<'a, 'ctx, 'env>(
|
|||
nullable_id,
|
||||
other_fields,
|
||||
} => {
|
||||
let tag_struct_type =
|
||||
block_of_memory_slices(env.context, &[other_fields], env.target_info);
|
||||
let roc_union =
|
||||
RocUnion::untagged_from_slices(env.context, &[other_fields], env.target_info);
|
||||
|
||||
if tag_id == *nullable_id as _ {
|
||||
let output_type = tag_struct_type.ptr_type(AddressSpace::Generic);
|
||||
let output_type = roc_union.struct_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
return output_type.const_null().into();
|
||||
}
|
||||
|
@ -1891,23 +1824,15 @@ fn build_tag<'a, 'ctx, 'env>(
|
|||
|
||||
debug_assert!(union_size == 2);
|
||||
|
||||
// Determine types
|
||||
let (field_types, field_values) = build_tag_fields(env, scope, other_fields, arguments);
|
||||
|
||||
// Create the struct_type
|
||||
let data_ptr =
|
||||
allocate_tag(env, parent, reuse_allocation, union_layout, &[other_fields]);
|
||||
|
||||
let struct_type = env
|
||||
.context
|
||||
.struct_type(field_types.into_bump_slice(), false);
|
||||
let data = build_struct(env, scope, arguments);
|
||||
|
||||
struct_pointer_from_fields(
|
||||
env,
|
||||
struct_type,
|
||||
data_ptr,
|
||||
field_values.into_iter().enumerate(),
|
||||
);
|
||||
let value = roc_union.as_struct_value(env, data, None);
|
||||
|
||||
env.builder.build_store(data_ptr, value);
|
||||
|
||||
data_ptr.into()
|
||||
}
|
||||
|
@ -2154,7 +2079,6 @@ fn lookup_at_index_ptr<'a, 'ctx, 'env>(
|
|||
fn lookup_at_index_ptr2<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
union_layout: &UnionLayout<'a>,
|
||||
tag_id_type: IntType<'ctx>,
|
||||
field_layouts: &[Layout<'_>],
|
||||
index: usize,
|
||||
value: PointerValue<'ctx>,
|
||||
|
@ -2164,23 +2088,15 @@ fn lookup_at_index_ptr2<'a, 'ctx, 'env>(
|
|||
let struct_layout = Layout::struct_no_name_order(field_layouts);
|
||||
let struct_type = basic_type_from_layout(env, &struct_layout);
|
||||
|
||||
let wrapper_type = env
|
||||
.context
|
||||
.struct_type(&[struct_type, tag_id_type.into()], false);
|
||||
|
||||
let ptr = env
|
||||
let data_ptr = env
|
||||
.builder
|
||||
.build_bitcast(
|
||||
value,
|
||||
wrapper_type.ptr_type(AddressSpace::Generic),
|
||||
struct_type.ptr_type(AddressSpace::Generic),
|
||||
"cast_lookup_at_index_ptr",
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
let data_ptr = builder
|
||||
.build_struct_gep(ptr, TAG_DATA_INDEX, "at_index_struct_gep_tag")
|
||||
.unwrap();
|
||||
|
||||
let elem_ptr = builder
|
||||
.build_struct_gep(data_ptr, index as u32, "at_index_struct_gep_data")
|
||||
.unwrap();
|
||||
|
@ -2224,35 +2140,18 @@ fn reserve_with_refcount_union_as_block_of_memory<'a, 'ctx, 'env>(
|
|||
) -> PointerValue<'ctx> {
|
||||
let ptr_bytes = env.target_info;
|
||||
|
||||
let block_type = block_of_memory_slices(env.context, fields, env.target_info);
|
||||
|
||||
let basic_type = if union_layout.stores_tag_id_as_data(ptr_bytes) {
|
||||
let tag_id_type = basic_type_from_layout(env, &union_layout.tag_id_layout());
|
||||
|
||||
env.context
|
||||
.struct_type(&[block_type, tag_id_type], false)
|
||||
.into()
|
||||
let roc_union = if union_layout.stores_tag_id_as_data(ptr_bytes) {
|
||||
RocUnion::tagged_from_slices(env.context, fields, env.target_info)
|
||||
} else {
|
||||
block_type
|
||||
RocUnion::untagged_from_slices(env.context, fields, env.target_info)
|
||||
};
|
||||
|
||||
let mut stack_size = fields
|
||||
.iter()
|
||||
.map(|tag| tag.iter().map(|l| l.stack_size(env.target_info)).sum())
|
||||
.max()
|
||||
.unwrap_or_default();
|
||||
|
||||
if union_layout.stores_tag_id_as_data(ptr_bytes) {
|
||||
stack_size += union_layout.tag_id_layout().stack_size(env.target_info);
|
||||
}
|
||||
|
||||
let alignment_bytes = fields
|
||||
.iter()
|
||||
.flat_map(|tag| tag.iter().map(|l| l.alignment_bytes(env.target_info)))
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
|
||||
reserve_with_refcount_help(env, basic_type, stack_size, alignment_bytes)
|
||||
reserve_with_refcount_help(
|
||||
env,
|
||||
roc_union.struct_type(),
|
||||
roc_union.tag_width(),
|
||||
roc_union.tag_alignment(),
|
||||
)
|
||||
}
|
||||
|
||||
fn reserve_with_refcount_help<'a, 'ctx, 'env>(
|
||||
|
@ -2655,10 +2554,9 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
|||
//
|
||||
// Hence, we explicitly memcpy source to destination, and rely on
|
||||
// LLVM optimizing away any inefficiencies.
|
||||
let size = env.ptr_int().const_int(
|
||||
layout.stack_size_without_alignment(env.target_info) as u64,
|
||||
false,
|
||||
);
|
||||
let target_info = env.target_info;
|
||||
let width = layout.stack_size(target_info);
|
||||
let size = env.ptr_int().const_int(width as _, false);
|
||||
|
||||
env.builder
|
||||
.build_memcpy(
|
||||
|
@ -3263,7 +3161,7 @@ fn get_tag_id_wrapped<'a, 'ctx, 'env>(
|
|||
) -> IntValue<'ctx> {
|
||||
let tag_id_ptr = env
|
||||
.builder
|
||||
.build_struct_gep(from_value, TAG_ID_INDEX, "tag_id_ptr")
|
||||
.build_struct_gep(from_value, RocUnion::TAG_ID_INDEX, "tag_id_ptr")
|
||||
.unwrap();
|
||||
|
||||
env.builder
|
||||
|
@ -3276,7 +3174,7 @@ pub fn get_tag_id_non_recursive<'a, 'ctx, 'env>(
|
|||
tag: StructValue<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
env.builder
|
||||
.build_extract_value(tag, TAG_ID_INDEX, "get_tag_id")
|
||||
.build_extract_value(tag, RocUnion::TAG_ID_INDEX, "get_tag_id")
|
||||
.unwrap()
|
||||
.into_int_value()
|
||||
}
|
||||
|
|
|
@ -2,9 +2,10 @@ use crate::llvm::build::Env;
|
|||
use bumpalo::collections::Vec;
|
||||
use inkwell::context::Context;
|
||||
use inkwell::types::{BasicType, BasicTypeEnum, FloatType, IntType, StructType};
|
||||
use inkwell::values::StructValue;
|
||||
use inkwell::AddressSpace;
|
||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||
use roc_mono::layout::{Builtin, Layout, UnionLayout};
|
||||
use roc_mono::layout::{round_up_to_alignment, Builtin, Layout, UnionLayout};
|
||||
use roc_target::TargetInfo;
|
||||
|
||||
fn basic_type_from_record<'a, 'ctx, 'env>(
|
||||
|
@ -56,36 +57,40 @@ pub fn basic_type_from_union_layout<'a, 'ctx, 'env>(
|
|||
) -> BasicTypeEnum<'ctx> {
|
||||
use UnionLayout::*;
|
||||
|
||||
let tag_id_type = basic_type_from_layout(env, &union_layout.tag_id_layout());
|
||||
|
||||
match union_layout {
|
||||
NonRecursive(tags) => {
|
||||
let data = block_of_memory_slices(env.context, tags, env.target_info);
|
||||
|
||||
env.context.struct_type(&[data, tag_id_type], false).into()
|
||||
//
|
||||
RocUnion::tagged_from_slices(env.context, tags, env.target_info)
|
||||
.struct_type()
|
||||
.into()
|
||||
}
|
||||
Recursive(tags)
|
||||
| NullableWrapped {
|
||||
other_tags: tags, ..
|
||||
} => {
|
||||
let data = block_of_memory_slices(env.context, tags, env.target_info);
|
||||
|
||||
if union_layout.stores_tag_id_as_data(env.target_info) {
|
||||
env.context
|
||||
.struct_type(&[data, tag_id_type], false)
|
||||
RocUnion::tagged_from_slices(env.context, tags, env.target_info)
|
||||
.struct_type()
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.into()
|
||||
} else {
|
||||
data.ptr_type(AddressSpace::Generic).into()
|
||||
RocUnion::untagged_from_slices(env.context, tags, env.target_info)
|
||||
.struct_type()
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
NullableUnwrapped { other_fields, .. } => {
|
||||
let block = block_of_memory_slices(env.context, &[other_fields], env.target_info);
|
||||
block.ptr_type(AddressSpace::Generic).into()
|
||||
RocUnion::untagged_from_slices(env.context, &[other_fields], env.target_info)
|
||||
.struct_type()
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.into()
|
||||
}
|
||||
NonNullableUnwrapped(fields) => {
|
||||
let block = block_of_memory_slices(env.context, &[fields], env.target_info);
|
||||
block.ptr_type(AddressSpace::Generic).into()
|
||||
RocUnion::untagged_from_slices(env.context, &[fields], env.target_info)
|
||||
.struct_type()
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -185,68 +190,196 @@ pub fn float_type_from_float_width<'a, 'ctx, 'env>(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn block_of_memory_slices<'ctx>(
|
||||
context: &'ctx Context,
|
||||
layouts: &[&[Layout<'_>]],
|
||||
target_info: TargetInfo,
|
||||
) -> BasicTypeEnum<'ctx> {
|
||||
let mut union_size = 0;
|
||||
for tag in layouts {
|
||||
let mut total = 0;
|
||||
for layout in tag.iter() {
|
||||
total += layout.stack_size(target_info);
|
||||
fn alignment_type(context: &Context, alignment: u32) -> BasicTypeEnum {
|
||||
match alignment {
|
||||
0 => context.struct_type(&[], false).into(),
|
||||
1 => context.i8_type().into(),
|
||||
2 => context.i16_type().into(),
|
||||
4 => context.i32_type().into(),
|
||||
8 => context.i64_type().into(),
|
||||
16 => context.i128_type().into(),
|
||||
_ => unimplemented!("weird alignment: {alignment}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum TagType {
|
||||
I8,
|
||||
I16,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) struct RocUnion<'ctx> {
|
||||
struct_type: StructType<'ctx>,
|
||||
data_align: u32,
|
||||
data_width: u32,
|
||||
tag_type: Option<TagType>,
|
||||
}
|
||||
|
||||
impl<'ctx> RocUnion<'ctx> {
|
||||
pub const TAG_ID_INDEX: u32 = 2;
|
||||
pub const TAG_DATA_INDEX: u32 = 1;
|
||||
|
||||
fn new(
|
||||
context: &'ctx Context,
|
||||
_target_info: TargetInfo,
|
||||
data_align: u32,
|
||||
data_width: u32,
|
||||
tag_type: Option<TagType>,
|
||||
) -> Self {
|
||||
let bytes = round_up_to_alignment(data_width, data_align);
|
||||
let byte_array_type = context.i8_type().array_type(bytes).as_basic_type_enum();
|
||||
|
||||
let alignment_array_type = alignment_type(context, data_align)
|
||||
.array_type(0)
|
||||
.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)
|
||||
};
|
||||
|
||||
Self {
|
||||
struct_type,
|
||||
data_align,
|
||||
data_width,
|
||||
tag_type,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn struct_type(&self) -> StructType<'ctx> {
|
||||
self.struct_type
|
||||
}
|
||||
|
||||
pub fn tagged_from_slices(
|
||||
context: &'ctx Context,
|
||||
layouts: &[&[Layout<'_>]],
|
||||
target_info: TargetInfo,
|
||||
) -> Self {
|
||||
let tag_type = match layouts.len() {
|
||||
0..=255 => TagType::I8,
|
||||
_ => TagType::I16,
|
||||
};
|
||||
|
||||
let (data_width, data_align) =
|
||||
Layout::stack_size_and_alignment_slices(layouts, target_info);
|
||||
|
||||
Self::new(context, target_info, data_align, data_width, Some(tag_type))
|
||||
}
|
||||
|
||||
pub fn untagged_from_slices(
|
||||
context: &'ctx Context,
|
||||
layouts: &[&[Layout<'_>]],
|
||||
target_info: TargetInfo,
|
||||
) -> Self {
|
||||
let (data_width, data_align) =
|
||||
Layout::stack_size_and_alignment_slices(layouts, target_info);
|
||||
|
||||
Self::new(context, target_info, data_align, data_width, None)
|
||||
}
|
||||
|
||||
pub fn tag_alignment(&self) -> u32 {
|
||||
let tag_id_alignment = match self.tag_type {
|
||||
None => 0,
|
||||
Some(TagType::I8) => 1,
|
||||
Some(TagType::I16) => 2,
|
||||
};
|
||||
|
||||
self.data_align.max(tag_id_alignment)
|
||||
}
|
||||
|
||||
pub fn tag_width(&self) -> u32 {
|
||||
let tag_id_width = match self.tag_type {
|
||||
None => 0,
|
||||
Some(TagType::I8) => 1,
|
||||
Some(TagType::I16) => 2,
|
||||
};
|
||||
|
||||
let mut width = self.data_width;
|
||||
|
||||
// add padding between data and the tag id
|
||||
width = round_up_to_alignment(width, tag_id_width);
|
||||
|
||||
// add tag id
|
||||
width += tag_id_width;
|
||||
|
||||
// add padding after the tag id
|
||||
width = round_up_to_alignment(width, self.tag_alignment());
|
||||
|
||||
width
|
||||
}
|
||||
|
||||
pub fn as_struct_value<'a, 'env>(
|
||||
&self,
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
data: StructValue<'ctx>,
|
||||
tag_id: Option<usize>,
|
||||
) -> StructValue<'ctx> {
|
||||
debug_assert_eq!(tag_id.is_some(), self.tag_type.is_some());
|
||||
|
||||
let mut struct_value = self.struct_type().const_zero();
|
||||
|
||||
let tag_alloca = env
|
||||
.builder
|
||||
.build_alloca(struct_value.get_type(), "tag_alloca");
|
||||
env.builder.build_store(tag_alloca, struct_value);
|
||||
|
||||
let cast_pointer = env.builder.build_pointer_cast(
|
||||
tag_alloca,
|
||||
data.get_type().ptr_type(AddressSpace::Generic),
|
||||
"to_data_ptr",
|
||||
);
|
||||
|
||||
env.builder.build_store(cast_pointer, data);
|
||||
|
||||
struct_value = env
|
||||
.builder
|
||||
.build_load(tag_alloca, "load_tag")
|
||||
.into_struct_value();
|
||||
|
||||
// set the tag id
|
||||
//
|
||||
// NOTE: setting the tag id initially happened before writing the data into it.
|
||||
// That turned out to expose UB. More info at https://github.com/rtfeldman/roc/issues/3554
|
||||
if let Some(tag_id) = tag_id {
|
||||
let tag_id_type = match self.tag_type.unwrap() {
|
||||
TagType::I8 => env.context.i8_type(),
|
||||
TagType::I16 => env.context.i16_type(),
|
||||
};
|
||||
|
||||
let tag_id = tag_id_type.const_int(tag_id as u64, false);
|
||||
|
||||
struct_value = env
|
||||
.builder
|
||||
.build_insert_value(struct_value, tag_id, Self::TAG_ID_INDEX, "insert_tag_id")
|
||||
.unwrap()
|
||||
.into_struct_value();
|
||||
}
|
||||
|
||||
union_size = union_size.max(total);
|
||||
}
|
||||
|
||||
block_of_memory_help(context, union_size)
|
||||
}
|
||||
|
||||
pub fn block_of_memory<'ctx>(
|
||||
context: &'ctx Context,
|
||||
layout: &Layout<'_>,
|
||||
target_info: TargetInfo,
|
||||
) -> BasicTypeEnum<'ctx> {
|
||||
// TODO make this dynamic
|
||||
let mut union_size = layout.stack_size(target_info);
|
||||
|
||||
if let Layout::Union(UnionLayout::NonRecursive { .. }) = layout {
|
||||
union_size -= target_info.ptr_width() as u32;
|
||||
}
|
||||
|
||||
block_of_memory_help(context, union_size)
|
||||
}
|
||||
|
||||
fn block_of_memory_help(context: &Context, union_size: u32) -> BasicTypeEnum<'_> {
|
||||
// The memory layout of Union is a bit tricky.
|
||||
// We have tags with different memory layouts, that are part of the same type.
|
||||
// For llvm, all tags must have the same memory layout.
|
||||
//
|
||||
// So, we convert all tags to a layout of bytes of some size.
|
||||
// It turns out that encoding to i64 for as many elements as possible is
|
||||
// a nice optimization, the remainder is encoded as bytes.
|
||||
|
||||
let num_i64 = union_size / 8;
|
||||
let num_i8 = union_size % 8;
|
||||
|
||||
let i8_array_type = context.i8_type().array_type(num_i8).as_basic_type_enum();
|
||||
let i64_array_type = context.i64_type().array_type(num_i64).as_basic_type_enum();
|
||||
|
||||
if num_i64 == 0 {
|
||||
// The object fits perfectly in some number of i8s
|
||||
context.struct_type(&[i8_array_type], false).into()
|
||||
} else if num_i8 == 0 {
|
||||
// The object fits perfectly in some number of i64s
|
||||
// (i.e. the size is a multiple of 8 bytes)
|
||||
context.struct_type(&[i64_array_type], false).into()
|
||||
} else {
|
||||
// There are some trailing bytes at the end
|
||||
let i8_array_type = context.i8_type().array_type(num_i8).as_basic_type_enum();
|
||||
|
||||
context
|
||||
.struct_type(&[i64_array_type, i8_array_type], false)
|
||||
.into()
|
||||
struct_value
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,10 +2,10 @@ use crate::debug_info_init;
|
|||
use crate::llvm::bitcode::call_void_bitcode_fn;
|
||||
use crate::llvm::build::{
|
||||
add_func, cast_basic_basic, get_tag_id, tag_pointer_clear_tag_id, use_roc_value, Env,
|
||||
FAST_CALL_CONV, TAG_DATA_INDEX, TAG_ID_INDEX,
|
||||
FAST_CALL_CONV,
|
||||
};
|
||||
use crate::llvm::build_list::{incrementing_elem_loop, list_len, load_list};
|
||||
use crate::llvm::convert::basic_type_from_layout;
|
||||
use crate::llvm::convert::{basic_type_from_layout, RocUnion};
|
||||
use bumpalo::collections::Vec;
|
||||
use inkwell::basic_block::BasicBlock;
|
||||
use inkwell::module::Linkage;
|
||||
|
@ -1597,7 +1597,7 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
|
|||
// read the tag_id
|
||||
let tag_id_ptr = env
|
||||
.builder
|
||||
.build_struct_gep(arg_ptr, TAG_ID_INDEX, "tag_id_ptr")
|
||||
.build_struct_gep(arg_ptr, RocUnion::TAG_ID_INDEX, "tag_id_ptr")
|
||||
.unwrap();
|
||||
|
||||
let tag_id = env
|
||||
|
@ -1634,7 +1634,7 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
|
|||
debug_assert!(wrapper_type.is_struct_type());
|
||||
let opaque_tag_data_ptr = env
|
||||
.builder
|
||||
.build_struct_gep(arg_ptr, TAG_DATA_INDEX, "field_ptr")
|
||||
.build_struct_gep(arg_ptr, RocUnion::TAG_DATA_INDEX, "field_ptr")
|
||||
.unwrap();
|
||||
|
||||
let cast_tag_data_pointer = env.builder.build_pointer_cast(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue