mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 13:59:08 +00:00
Move roc struct operations to a new "struct" module
This commit is contained in:
parent
1646dc4e73
commit
470ed119c2
6 changed files with 228 additions and 135 deletions
|
@ -1,8 +1,8 @@
|
|||
/// Helpers for interacting with the zig that generates bitcode
|
||||
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,
|
||||
complex_bitcast_check_size, load_roc_value, to_cc_return, CCReturn, Env, C_CALL_CONV,
|
||||
FAST_CALL_CONV,
|
||||
};
|
||||
use crate::llvm::convert::basic_type_from_layout;
|
||||
use crate::llvm::refcounting::{
|
||||
|
@ -23,6 +23,7 @@ use roc_mono::layout::{
|
|||
|
||||
use super::build::{create_entry_block_alloca, BuilderExt};
|
||||
use super::convert::zig_list_type;
|
||||
use super::struct_::struct_from_fields;
|
||||
|
||||
pub fn call_bitcode_fn<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
|
|
|
@ -7,6 +7,7 @@ use crate::llvm::expect::{clone_to_shared_memory, SharedMemoryPointer};
|
|||
use crate::llvm::refcounting::{
|
||||
build_reset, decrement_refcount_layout, increment_refcount_layout, PointerToRefcount,
|
||||
};
|
||||
use crate::llvm::struct_::{self, struct_from_fields, RocStruct};
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use inkwell::attributes::{Attribute, AttributeLoc};
|
||||
|
@ -22,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::{self};
|
||||
use inkwell::values::{
|
||||
BasicMetadataValueEnum, CallSiteValue, FunctionValue, InstructionValue, IntValue, PhiValue,
|
||||
PointerValue, StructValue,
|
||||
|
@ -1013,29 +1014,6 @@ pub fn build_exp_call<'a, 'ctx>(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn struct_from_fields<'a, 'ctx, 'env, I>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
struct_type: StructType<'ctx>,
|
||||
values: I,
|
||||
) -> StructValue<'ctx>
|
||||
where
|
||||
I: Iterator<Item = (usize, BasicValueEnum<'ctx>)>,
|
||||
{
|
||||
let mut struct_value = struct_type.const_zero().into();
|
||||
|
||||
// Insert field exprs into struct_val
|
||||
for (index, field_val) in values {
|
||||
let index: u32 = index as u32;
|
||||
|
||||
struct_value = env
|
||||
.builder
|
||||
.build_insert_value(struct_value, field_val, index, "insert_record_field")
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
struct_value.into_struct_value()
|
||||
}
|
||||
|
||||
fn struct_pointer_from_fields<'a, 'ctx, 'env, I>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
|
@ -1097,7 +1075,9 @@ pub fn build_exp_expr<'a, 'ctx>(
|
|||
call,
|
||||
),
|
||||
|
||||
Struct(sorted_fields) => build_struct(env, layout_interner, scope, sorted_fields).into(),
|
||||
Struct(sorted_fields) => {
|
||||
RocStruct::build(env, layout_interner, scope, sorted_fields).into()
|
||||
}
|
||||
|
||||
Reuse {
|
||||
arguments,
|
||||
|
@ -1323,45 +1303,7 @@ pub fn build_exp_expr<'a, 'ctx>(
|
|||
} => {
|
||||
let (value, layout) = load_symbol_and_layout(scope, structure);
|
||||
|
||||
let layout = if let LayoutRepr::LambdaSet(lambda_set) = layout_interner.get_repr(layout)
|
||||
{
|
||||
lambda_set.runtime_representation()
|
||||
} else {
|
||||
layout
|
||||
};
|
||||
|
||||
// extract field from a record
|
||||
match (value, layout_interner.get_repr(layout)) {
|
||||
(StructValue(argument), LayoutRepr::Struct(field_layouts)) => {
|
||||
debug_assert!(!field_layouts.is_empty());
|
||||
|
||||
let field_value = env
|
||||
.builder
|
||||
.build_extract_value(
|
||||
argument,
|
||||
*index as u32,
|
||||
env.arena
|
||||
.alloc(format!("struct_field_access_record_{}", index)),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let field_layout = field_layouts[*index as usize];
|
||||
use_roc_value(
|
||||
env,
|
||||
layout_interner,
|
||||
field_layout,
|
||||
field_value,
|
||||
"struct_field_tag",
|
||||
)
|
||||
}
|
||||
(other, layout) => {
|
||||
// potential cause: indexing into an unwrapped 1-element record/tag?
|
||||
unreachable!(
|
||||
"can only index into struct layout\nValue: {:?}\nLayout: {:?}\nIndex: {:?}",
|
||||
other, layout, index
|
||||
)
|
||||
}
|
||||
}
|
||||
struct_::load_at_index(env, layout_interner, layout, value, *index)
|
||||
}
|
||||
|
||||
EmptyArray => empty_polymorphic_list(env),
|
||||
|
@ -1679,51 +1621,6 @@ fn build_tag_fields<'a, 'r, 'ctx, 'env>(
|
|||
(field_types, field_values)
|
||||
}
|
||||
|
||||
fn build_struct<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
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 !layout_interner
|
||||
.get_repr(field_layout)
|
||||
.is_dropped_because_empty()
|
||||
{
|
||||
let field_type = basic_type_from_layout(env, layout_interner, field_layout);
|
||||
field_types.push(field_type);
|
||||
|
||||
if layout_interner.is_passed_by_reference(field_layout) {
|
||||
let field_value = env.builder.new_build_load(
|
||||
field_type,
|
||||
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<'a, 'ctx, '_>,
|
||||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
|
@ -1740,7 +1637,7 @@ fn build_tag<'a, 'ctx>(
|
|||
UnionLayout::NonRecursive(tags) => {
|
||||
debug_assert!(union_size > 1);
|
||||
|
||||
let data = build_struct(env, layout_interner, scope, arguments);
|
||||
let data = RocStruct::build(env, layout_interner, scope, arguments);
|
||||
|
||||
let roc_union =
|
||||
RocUnion::tagged_from_slices(layout_interner, env.context, tags, env.target_info);
|
||||
|
@ -1873,7 +1770,7 @@ fn build_tag<'a, 'ctx>(
|
|||
&[other_fields],
|
||||
);
|
||||
|
||||
let data = build_struct(env, layout_interner, scope, arguments);
|
||||
let data = RocStruct::build(env, layout_interner, scope, arguments);
|
||||
|
||||
let value = roc_union.as_struct_value(env, data, None);
|
||||
|
||||
|
|
|
@ -14,10 +14,10 @@ use roc_mono::layout::{Builtin, InLayout, LayoutIds, LayoutInterner, STLayoutInt
|
|||
|
||||
use super::bitcode::{call_list_bitcode_fn, BitcodeReturns};
|
||||
use super::build::{
|
||||
create_entry_block_alloca, load_roc_value, load_symbol, store_roc_value, struct_from_fields,
|
||||
BuilderExt,
|
||||
create_entry_block_alloca, load_roc_value, load_symbol, store_roc_value, BuilderExt,
|
||||
};
|
||||
use super::convert::zig_list_type;
|
||||
use super::struct_::struct_from_fields;
|
||||
|
||||
fn call_list_bitcode_fn_1<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::llvm::build::{BuilderExt, Env};
|
||||
use bumpalo::collections::Vec;
|
||||
use crate::llvm::struct_::RocStructType;
|
||||
use inkwell::context::Context;
|
||||
use inkwell::types::{BasicType, BasicTypeEnum, FloatType, IntType, StructType};
|
||||
use inkwell::values::StructValue;
|
||||
|
@ -11,23 +11,7 @@ use roc_mono::layout::{
|
|||
};
|
||||
use roc_target::TargetInfo;
|
||||
|
||||
fn basic_type_from_record<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
fields: &[InLayout<'_>],
|
||||
) -> BasicTypeEnum<'ctx> {
|
||||
let mut field_types = Vec::with_capacity_in(fields.len(), env.arena);
|
||||
|
||||
for field_layout in fields.iter() {
|
||||
let typ = basic_type_from_layout(env, layout_interner, *field_layout);
|
||||
|
||||
field_types.push(typ);
|
||||
}
|
||||
|
||||
env.context
|
||||
.struct_type(field_types.into_bump_slice(), false)
|
||||
.as_basic_type_enum()
|
||||
}
|
||||
use super::struct_::RocStruct;
|
||||
|
||||
pub fn basic_type_from_layout<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
|
@ -37,7 +21,9 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>(
|
|||
use LayoutRepr::*;
|
||||
|
||||
match layout_interner.get_repr(layout) {
|
||||
Struct(sorted_fields, ..) => basic_type_from_record(env, layout_interner, sorted_fields),
|
||||
Struct(sorted_fields, ..) => {
|
||||
RocStructType::build(env, layout_interner, sorted_fields).into()
|
||||
}
|
||||
LambdaSet(lambda_set) => {
|
||||
basic_type_from_layout(env, layout_interner, lambda_set.runtime_representation())
|
||||
}
|
||||
|
@ -362,11 +348,13 @@ impl<'ctx> RocUnion<'ctx> {
|
|||
pub fn as_struct_value<'a, 'env>(
|
||||
&self,
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
data: StructValue<'ctx>,
|
||||
data: RocStruct<'ctx>,
|
||||
tag_id: Option<usize>,
|
||||
) -> StructValue<'ctx> {
|
||||
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 data_buffer = env
|
||||
|
|
|
@ -9,3 +9,5 @@ pub mod externs;
|
|||
mod intrinsics;
|
||||
mod lowlevel;
|
||||
pub mod refcounting;
|
||||
|
||||
mod struct_;
|
||||
|
|
205
crates/compiler/gen_llvm/src/llvm/struct_.rs
Normal file
205
crates/compiler/gen_llvm/src/llvm/struct_.rs
Normal file
|
@ -0,0 +1,205 @@
|
|||
//! Representation of structs in the generated LLVM IR.
|
||||
|
||||
use bumpalo::collections::Vec as AVec;
|
||||
use inkwell::{
|
||||
types::{BasicType, BasicTypeEnum, StructType},
|
||||
values::{BasicValue, BasicValueEnum, StructValue},
|
||||
};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::layout::{InLayout, LayoutInterner, LayoutRepr, STLayoutInterner};
|
||||
|
||||
use crate::llvm::build::use_roc_value;
|
||||
|
||||
use super::{
|
||||
build::{load_symbol_and_layout, BuilderExt, Env, Scope},
|
||||
convert::basic_type_from_layout,
|
||||
};
|
||||
|
||||
pub enum RocStructType<'ctx> {
|
||||
/// The roc struct should be passed by rvalue.
|
||||
ByValue(StructType<'ctx>),
|
||||
}
|
||||
|
||||
impl<'ctx> Into<BasicTypeEnum<'ctx>> for RocStructType<'ctx> {
|
||||
fn into(self) -> BasicTypeEnum<'ctx> {
|
||||
self.as_basic_type_enum()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> RocStructType<'ctx> {
|
||||
pub fn build<'a>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
fields: &[InLayout<'_>],
|
||||
) -> Self {
|
||||
let struct_type = basic_type_from_record(env, layout_interner, fields);
|
||||
RocStructType::ByValue(struct_type)
|
||||
}
|
||||
|
||||
pub fn as_basic_type_enum(&self) -> BasicTypeEnum<'ctx> {
|
||||
match self {
|
||||
RocStructType::ByValue(struct_type) => struct_type.as_basic_type_enum(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn basic_type_from_record<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
fields: &[InLayout<'_>],
|
||||
) -> StructType<'ctx> {
|
||||
let mut field_types = AVec::with_capacity_in(fields.len(), env.arena);
|
||||
|
||||
for field_layout in fields.iter() {
|
||||
let typ = basic_type_from_layout(env, layout_interner, *field_layout);
|
||||
|
||||
field_types.push(typ);
|
||||
}
|
||||
|
||||
env.context
|
||||
.struct_type(field_types.into_bump_slice(), false)
|
||||
}
|
||||
|
||||
pub enum RocStruct<'ctx> {
|
||||
/// The roc struct should be passed by rvalue.
|
||||
ByValue(StructValue<'ctx>),
|
||||
}
|
||||
|
||||
impl<'ctx> Into<BasicValueEnum<'ctx>> for RocStruct<'ctx> {
|
||||
fn into(self) -> BasicValueEnum<'ctx> {
|
||||
self.as_basic_value_enum()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> RocStruct<'ctx> {
|
||||
pub fn build<'a>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
sorted_fields: &[Symbol],
|
||||
) -> Self {
|
||||
let struct_val = build_struct_value(env, layout_interner, scope, sorted_fields);
|
||||
RocStruct::ByValue(struct_val)
|
||||
}
|
||||
|
||||
pub fn as_basic_value_enum(&self) -> BasicValueEnum<'ctx> {
|
||||
match self {
|
||||
RocStruct::ByValue(struct_val) => struct_val.as_basic_value_enum(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_struct_value<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
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 = AVec::with_capacity_in(num_fields, env.arena);
|
||||
let mut field_vals = AVec::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 !layout_interner
|
||||
.get_repr(field_layout)
|
||||
.is_dropped_because_empty()
|
||||
{
|
||||
let field_type = basic_type_from_layout(env, layout_interner, field_layout);
|
||||
field_types.push(field_type);
|
||||
|
||||
if layout_interner.is_passed_by_reference(field_layout) {
|
||||
let field_value = env.builder.new_build_load(
|
||||
field_type,
|
||||
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())
|
||||
}
|
||||
|
||||
pub fn struct_from_fields<'a, 'ctx, 'env, I>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
struct_type: StructType<'ctx>,
|
||||
values: I,
|
||||
) -> StructValue<'ctx>
|
||||
where
|
||||
I: Iterator<Item = (usize, BasicValueEnum<'ctx>)>,
|
||||
{
|
||||
let mut struct_value = struct_type.const_zero().into();
|
||||
|
||||
// Insert field exprs into struct_val
|
||||
for (index, field_val) in values {
|
||||
let index: u32 = index as u32;
|
||||
|
||||
struct_value = env
|
||||
.builder
|
||||
.build_insert_value(struct_value, field_val, index, "insert_record_field")
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
struct_value.into_struct_value()
|
||||
}
|
||||
|
||||
pub fn load_at_index<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
layout: InLayout<'a>,
|
||||
value: BasicValueEnum<'ctx>,
|
||||
index: u64,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let layout = if let LayoutRepr::LambdaSet(lambda_set) = layout_interner.get_repr(layout) {
|
||||
lambda_set.runtime_representation()
|
||||
} else {
|
||||
layout
|
||||
};
|
||||
|
||||
// extract field from a record
|
||||
match (value, layout_interner.get_repr(layout)) {
|
||||
(BasicValueEnum::StructValue(argument), LayoutRepr::Struct(field_layouts)) => {
|
||||
debug_assert!(!field_layouts.is_empty());
|
||||
|
||||
let field_value = env
|
||||
.builder
|
||||
.build_extract_value(
|
||||
argument,
|
||||
index as u32,
|
||||
env.arena
|
||||
.alloc(format!("struct_field_access_record_{}", index)),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let field_layout = field_layouts[index as usize];
|
||||
use_roc_value(
|
||||
env,
|
||||
layout_interner,
|
||||
field_layout,
|
||||
field_value,
|
||||
"struct_field_tag",
|
||||
)
|
||||
}
|
||||
(other, layout) => {
|
||||
// potential cause: indexing into an unwrapped 1-element record/tag?
|
||||
unreachable!(
|
||||
"can only index into struct layout\nValue: {:?}\nLayout: {:?}\nIndex: {:?}",
|
||||
other, layout, index
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue