mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-24 12:32:29 +00:00
Implement struct-by-reference for structs over 4 machine words
This commit is contained in:
parent
e1c874ac23
commit
47fb9fe6f2
5 changed files with 83 additions and 23 deletions
|
@ -1042,9 +1042,14 @@ pub(crate) fn build_exp_expr<'a, 'ctx>(
|
||||||
call,
|
call,
|
||||||
),
|
),
|
||||||
|
|
||||||
Struct(sorted_fields) => {
|
Struct(sorted_fields) => RocStruct::build(
|
||||||
RocStruct::build(env, layout_interner, scope, sorted_fields).into()
|
env,
|
||||||
}
|
layout_interner,
|
||||||
|
layout_interner.get_repr(layout),
|
||||||
|
scope,
|
||||||
|
sorted_fields,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
|
||||||
Reuse {
|
Reuse {
|
||||||
arguments,
|
arguments,
|
||||||
|
@ -1605,7 +1610,8 @@ fn build_tag<'a, 'ctx>(
|
||||||
UnionLayout::NonRecursive(tags) => {
|
UnionLayout::NonRecursive(tags) => {
|
||||||
debug_assert!(union_size > 1);
|
debug_assert!(union_size > 1);
|
||||||
|
|
||||||
let data = RocStruct::build(env, layout_interner, scope, arguments);
|
let data_layout_repr = LayoutRepr::Struct(tags[tag_id as usize]);
|
||||||
|
let data = RocStruct::build(env, layout_interner, data_layout_repr, scope, arguments);
|
||||||
|
|
||||||
let roc_union =
|
let roc_union =
|
||||||
RocUnion::tagged_from_slices(layout_interner, env.context, tags, env.target_info);
|
RocUnion::tagged_from_slices(layout_interner, env.context, tags, env.target_info);
|
||||||
|
@ -1738,7 +1744,8 @@ fn build_tag<'a, 'ctx>(
|
||||||
&[other_fields],
|
&[other_fields],
|
||||||
);
|
);
|
||||||
|
|
||||||
let data = RocStruct::build(env, layout_interner, scope, arguments);
|
let data_layout_repr = LayoutRepr::Struct(other_fields);
|
||||||
|
let data = RocStruct::build(env, layout_interner, data_layout_repr, scope, arguments);
|
||||||
|
|
||||||
let value = roc_union.as_struct_value(env, data, None);
|
let value = roc_union.as_struct_value(env, data, None);
|
||||||
|
|
||||||
|
|
|
@ -353,8 +353,6 @@ impl<'ctx> RocUnion<'ctx> {
|
||||||
) -> StructValue<'ctx> {
|
) -> StructValue<'ctx> {
|
||||||
debug_assert_eq!(tag_id.is_some(), self.tag_type.is_some());
|
debug_assert_eq!(tag_id.is_some(), self.tag_type.is_some());
|
||||||
|
|
||||||
let RocStruct::ByValue(data) = data;
|
|
||||||
|
|
||||||
let tag_alloca = env.builder.build_alloca(self.struct_type(), "tag_alloca");
|
let tag_alloca = env.builder.build_alloca(self.struct_type(), "tag_alloca");
|
||||||
|
|
||||||
let data_buffer = env
|
let data_buffer = env
|
||||||
|
@ -367,16 +365,36 @@ impl<'ctx> RocUnion<'ctx> {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let cast_pointer = env.builder.build_pointer_cast(
|
match data {
|
||||||
data_buffer,
|
// NOTE: the data may be smaller than the buffer, so there might be uninitialized
|
||||||
data.get_type().ptr_type(AddressSpace::default()),
|
// bytes in the buffer. We should never touch those, but e.g. valgrind might not
|
||||||
"to_data_ptr",
|
// realize that. If that comes up, the solution is to just fill it with zeros
|
||||||
);
|
RocStruct::ByValue(value) => {
|
||||||
|
let cast_pointer = env.builder.build_pointer_cast(
|
||||||
|
data_buffer,
|
||||||
|
value.get_type().ptr_type(AddressSpace::default()),
|
||||||
|
"to_data_ptr",
|
||||||
|
);
|
||||||
|
env.builder.build_store(cast_pointer, value);
|
||||||
|
}
|
||||||
|
RocStruct::ByReference(ptr) => {
|
||||||
|
let cast_pointer =
|
||||||
|
env.builder
|
||||||
|
.build_pointer_cast(data_buffer, ptr.get_type(), "to_data_ptr");
|
||||||
|
|
||||||
// NOTE: the data may be smaller than the buffer, so there might be uninitialized
|
env.builder
|
||||||
// bytes in the buffer. We should never touch those, but e.g. valgrind might not
|
.build_memcpy(
|
||||||
// realize that. If that comes up, the solution is to just fill it with zeros
|
cast_pointer,
|
||||||
env.builder.build_store(cast_pointer, data);
|
self.data_align,
|
||||||
|
ptr,
|
||||||
|
self.data_align,
|
||||||
|
env.context
|
||||||
|
.i32_type()
|
||||||
|
.const_int(self.data_width as _, false),
|
||||||
|
)
|
||||||
|
.expect("memcpy invariants must have been upheld");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// set the tag id
|
// set the tag id
|
||||||
//
|
//
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use bumpalo::collections::Vec as AVec;
|
use bumpalo::collections::Vec as AVec;
|
||||||
use inkwell::{
|
use inkwell::{
|
||||||
types::{BasicType, BasicTypeEnum, StructType},
|
types::{BasicType, BasicTypeEnum, StructType},
|
||||||
values::{BasicValue, BasicValueEnum, StructValue},
|
values::{BasicValue, BasicValueEnum, PointerValue, StructValue},
|
||||||
};
|
};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_mono::layout::{InLayout, LayoutInterner, LayoutRepr, STLayoutInterner};
|
use roc_mono::layout::{InLayout, LayoutInterner, LayoutRepr, STLayoutInterner};
|
||||||
|
@ -65,6 +65,9 @@ fn basic_type_from_record<'a, 'ctx>(
|
||||||
pub(crate) enum RocStruct<'ctx> {
|
pub(crate) enum RocStruct<'ctx> {
|
||||||
/// The roc struct should be passed by rvalue.
|
/// The roc struct should be passed by rvalue.
|
||||||
ByValue(StructValue<'ctx>),
|
ByValue(StructValue<'ctx>),
|
||||||
|
|
||||||
|
/// The roc struct should be passed by reference.
|
||||||
|
ByReference(PointerValue<'ctx>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx> Into<BasicValueEnum<'ctx>> for RocStruct<'ctx> {
|
impl<'ctx> Into<BasicValueEnum<'ctx>> for RocStruct<'ctx> {
|
||||||
|
@ -87,16 +90,30 @@ impl<'ctx> RocStruct<'ctx> {
|
||||||
pub fn build<'a>(
|
pub fn build<'a>(
|
||||||
env: &Env<'a, 'ctx, '_>,
|
env: &Env<'a, 'ctx, '_>,
|
||||||
layout_interner: &mut STLayoutInterner<'a>,
|
layout_interner: &mut STLayoutInterner<'a>,
|
||||||
|
layout_repr: LayoutRepr<'a>,
|
||||||
scope: &Scope<'a, 'ctx>,
|
scope: &Scope<'a, 'ctx>,
|
||||||
sorted_fields: &[Symbol],
|
sorted_fields: &[Symbol],
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let struct_val = build_struct_value(env, layout_interner, scope, sorted_fields);
|
let BuildStruct {
|
||||||
RocStruct::ByValue(struct_val)
|
struct_type,
|
||||||
|
struct_val,
|
||||||
|
} = build_struct_helper(env, layout_interner, scope, sorted_fields);
|
||||||
|
|
||||||
|
let passed_by_ref = layout_repr.is_passed_by_reference(layout_interner, env.target_info);
|
||||||
|
|
||||||
|
if passed_by_ref {
|
||||||
|
let alloca = env.builder.build_alloca(struct_type, "struct_alloca");
|
||||||
|
env.builder.build_store(alloca, struct_val);
|
||||||
|
RocStruct::ByReference(alloca)
|
||||||
|
} else {
|
||||||
|
RocStruct::ByValue(struct_val)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_basic_value_enum(&self) -> BasicValueEnum<'ctx> {
|
pub fn as_basic_value_enum(&self) -> BasicValueEnum<'ctx> {
|
||||||
match self {
|
match self {
|
||||||
RocStruct::ByValue(struct_val) => struct_val.as_basic_value_enum(),
|
RocStruct::ByValue(struct_val) => struct_val.as_basic_value_enum(),
|
||||||
|
RocStruct::ByReference(struct_ptr) => struct_ptr.as_basic_value_enum(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,12 +174,17 @@ fn index_struct_value<'a, 'ctx>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_struct_value<'a, 'ctx>(
|
struct BuildStruct<'ctx> {
|
||||||
|
struct_type: StructType<'ctx>,
|
||||||
|
struct_val: StructValue<'ctx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_struct_helper<'a, 'ctx>(
|
||||||
env: &Env<'a, 'ctx, '_>,
|
env: &Env<'a, 'ctx, '_>,
|
||||||
layout_interner: &mut STLayoutInterner<'a>,
|
layout_interner: &mut STLayoutInterner<'a>,
|
||||||
scope: &Scope<'a, 'ctx>,
|
scope: &Scope<'a, 'ctx>,
|
||||||
sorted_fields: &[Symbol],
|
sorted_fields: &[Symbol],
|
||||||
) -> StructValue<'ctx> {
|
) -> BuildStruct<'ctx> {
|
||||||
let ctx = env.context;
|
let ctx = env.context;
|
||||||
|
|
||||||
// Determine types
|
// Determine types
|
||||||
|
@ -197,9 +219,12 @@ fn build_struct_value<'a, 'ctx>(
|
||||||
|
|
||||||
// Create the struct_type
|
// Create the struct_type
|
||||||
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
||||||
|
let struct_val = struct_from_fields(env, struct_type, field_vals.into_iter().enumerate());
|
||||||
|
|
||||||
// Insert field exprs into struct_val
|
BuildStruct {
|
||||||
struct_from_fields(env, struct_type, field_vals.into_iter().enumerate())
|
struct_type,
|
||||||
|
struct_val,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn struct_from_fields<'a, 'ctx, 'env, I>(
|
pub fn struct_from_fields<'a, 'ctx, 'env, I>(
|
||||||
|
|
|
@ -2617,6 +2617,11 @@ impl<'a> LayoutRepr<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LayoutRepr::Union(UnionLayout::NonRecursive(_)) => true,
|
LayoutRepr::Union(UnionLayout::NonRecursive(_)) => true,
|
||||||
|
LayoutRepr::Struct(_) => {
|
||||||
|
// TODO: write tests for this!
|
||||||
|
self.stack_size(interner, target_info) as usize > target_info.max_by_value_size()
|
||||||
|
}
|
||||||
|
|
||||||
LayoutRepr::LambdaSet(lambda_set) => interner
|
LayoutRepr::LambdaSet(lambda_set) => interner
|
||||||
.get_repr(lambda_set.runtime_representation())
|
.get_repr(lambda_set.runtime_representation())
|
||||||
.is_passed_by_reference(interner, target_info),
|
.is_passed_by_reference(interner, target_info),
|
||||||
|
|
|
@ -67,6 +67,11 @@ impl TargetInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn max_by_value_size(&self) -> usize {
|
||||||
|
// Pass values larger than 4 machine words by reference.
|
||||||
|
self.ptr_size() * 4
|
||||||
|
}
|
||||||
|
|
||||||
pub const fn ptr_alignment_bytes(&self) -> usize {
|
pub const fn ptr_alignment_bytes(&self) -> usize {
|
||||||
self.architecture.ptr_alignment_bytes()
|
self.architecture.ptr_alignment_bytes()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue