Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 47 additions & 91 deletions crates/mun_codegen/src/ir/array.rs
Original file line number Diff line number Diff line change
@@ -1,127 +1,83 @@
//! Defines a helper struct `RuntimeArrayValue` which wraps an inkwell value and
//! represents a pointer to a heap allocated Mun array struct.
//! Typed projections into heap-allocated Mun arrays.
//!
//! Mun arrays are represented on the heap as:
//!
//! ```c
//! struct Obj {
//! ArrayValueT *value;
//! ...
//! }
//!
//! struct ArrayValueT {
//! usize_t len;
//! usize_t capacity;
//! T elements[capacity];
//! }
//! ```

use std::ffi::CStr;
//! A runtime array handle points indirectly to `{ length, capacity,
//! first_element }`. This module keeps that aggregate type explicit so
//! projections do not depend on LLVM pointer element types.

use inkwell::{
builder::Builder,
types::{BasicTypeEnum, IntType, StructType},
values::{BasicValueEnum, IntValue, PointerValue},
values::{BasicValueEnum, PointerValue},
};

use crate::ir::reference::RuntimeReferenceValue;
use crate::ir::{reference::RuntimeReferenceValue, value::PlaceValue};

/// A helper struct that wraps a [`PointerValue`] which points to an in memory
/// Mun array value.
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub struct RuntimeArrayValue<'ink>(RuntimeReferenceValue<'ink>);
/// A runtime array handle paired with the concrete array aggregate type.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub(crate) struct RuntimeArrayValue<'ink> {
reference: RuntimeReferenceValue<'ink>,
array_type: StructType<'ink>,
}

impl<'ink> RuntimeArrayValue<'ink> {
/// Constructs a new `RuntimeArrayValue` from a reference pointer to a
/// specific array type.
///
/// The pointer passed must be of type `**ArrayValueT`.
pub fn from_ptr(ptr: PointerValue<'ink>, array_type: StructType<'ink>) -> Result<Self, String> {
RuntimeReferenceValue::from_ptr(ptr, array_type).map(Self)
}

/// Constructs a new instance from an inkwell [`PointerValue`] without
/// checking if this is actually a pointer to an array.
pub unsafe fn from_ptr_unchecked(ptr: PointerValue<'ink>) -> Self {
Self(RuntimeReferenceValue::from_ptr_unchecked(ptr))
}

/// Returns the name of the array
pub fn get_name(&self) -> &CStr {
self.0.get_name()
/// Associates a runtime handle with its concrete array aggregate type.
pub(crate) fn new(pointer: PointerValue<'ink>, array_type: StructType<'ink>) -> Self {
Self {
reference: RuntimeReferenceValue::new(pointer, array_type.into()),
array_type,
}
}

/// Generate code to get to the array value.
fn get_array_ptr(&self, builder: &Builder<'ink>) -> PointerValue<'ink> {
self.0.get_data_ptr(builder)
fn get_array(&self, builder: &Builder<'ink>) -> PlaceValue<'ink> {
self.reference.get_data(builder)
}

/// Generate code to fetch the length of the array.
pub fn get_length_ptr(&self, builder: &Builder<'ink>) -> PointerValue<'ink> {
let array_ptr = self.get_array_ptr(builder);
let value_name = array_ptr.get_name().to_string_lossy();
builder
.build_struct_gep(array_ptr, 0, &format!("{value_name}->length"))
.expect("could not get `length` from array struct")
}

/// Generate code to fetch the capacity of the array.
pub fn get_capacity(&self, builder: &Builder<'ink>) -> IntValue<'ink> {
let array_ptr = self.get_array_ptr(builder);
let value_name = array_ptr.get_name().to_string_lossy();
let length_ptr = builder
.build_struct_gep(array_ptr, 1, &format!("{value_name}->capacity"))
/// Projects the array length field.
pub(crate) fn get_length(&self, builder: &Builder<'ink>) -> PlaceValue<'ink> {
let array = self.get_array(builder);
let pointer = array.pointer();
let value_name = pointer.get_name().to_string_lossy();
let pointer = builder
.build_struct_gep(pointer, 0, &format!("{value_name}->length"))
.expect("could not get `length` from array struct");
builder
.build_load(length_ptr, &format!("{value_name}.capacity"))
.into_int_value()
PlaceValue::new(pointer, self.length_ty().into())
}

/// Generate code to a pointer to the elements stored in the array.
pub fn get_elements(&self, builder: &Builder<'ink>) -> PointerValue<'ink> {
let array_ptr = self.get_array_ptr(builder);
let value_name = array_ptr.get_name().to_string_lossy();
builder
.build_struct_gep(array_ptr, 2, &format!("{value_name}->elements"))
.expect("could not get `elements` from array struct")
/// Projects the first array element.
pub(crate) fn get_elements(&self, builder: &Builder<'ink>) -> PlaceValue<'ink> {
let array = self.get_array(builder);
let pointer = array.pointer();
let value_name = pointer.get_name().to_string_lossy();
let pointer = builder
.build_struct_gep(pointer, 2, &format!("{value_name}->elements"))
.expect("could not get `elements` from array struct");
PlaceValue::new(pointer, self.element_ty())
}

/// Returns the type of the `length` field
pub fn length_ty(&self) -> IntType<'_> {
self.array_data_ty()
/// Returns the type of the length field.
pub(crate) fn length_ty(&self) -> IntType<'ink> {
self.array_type
.get_field_type_at_index(0)
.expect("an array must have a second field")
.expect("an array must have a length field")
.into_int_type()
}

/// Returns the type of the `length` field
pub fn capacity_ty(&self) -> IntType<'_> {
self.array_data_ty()
.get_field_type_at_index(1)
.expect("an array must have a second field")
.into_int_type()
}

/// Returns the type of the elements stored in this array
pub fn element_ty(&self) -> BasicTypeEnum<'ink> {
self.array_data_ty()
/// Returns the type of an array element.
pub(crate) fn element_ty(&self) -> BasicTypeEnum<'ink> {
self.array_type
.get_field_type_at_index(2)
.expect("an array must have a second field")
}

fn array_data_ty(&self) -> StructType<'ink> {
self.0.get_type().into_struct_type()
.expect("an array must have an element field")
}
}

impl<'ink> From<RuntimeArrayValue<'ink>> for BasicValueEnum<'ink> {
fn from(value: RuntimeArrayValue<'ink>) -> Self {
value.0.into()
value.reference.into()
}
}

impl<'ink> From<RuntimeArrayValue<'ink>> for PointerValue<'ink> {
fn from(value: RuntimeArrayValue<'ink>) -> Self {
value.0.into()
value.reference.into()
}
}
Loading
Loading