From 9d9ead211eecb411a26a7ff3be17a1d410073be4 Mon Sep 17 00:00:00 2001 From: David Mak Date: Tue, 29 Oct 2024 13:57:28 +0800 Subject: [PATCH] [core] Move Proxies to their own modules --- nac3artiq/src/codegen.rs | 9 +- nac3artiq/src/symbol_resolver.rs | 2 +- nac3core/src/codegen/builtin_fns.rs | 8 +- nac3core/src/codegen/classes.rs | 1768 ------------------------ nac3core/src/codegen/expr.rs | 9 +- nac3core/src/codegen/generator.rs | 2 +- nac3core/src/codegen/irrt/mod.rs | 8 +- nac3core/src/codegen/mod.rs | 5 +- nac3core/src/codegen/numpy.rs | 11 +- nac3core/src/codegen/stmt.rs | 2 +- nac3core/src/codegen/test.rs | 2 +- nac3core/src/codegen/types/list.rs | 163 +++ nac3core/src/codegen/types/mod.rs | 50 + nac3core/src/codegen/types/ndarray.rs | 191 +++ nac3core/src/codegen/types/range.rs | 132 ++ nac3core/src/codegen/values/array.rs | 426 ++++++ nac3core/src/codegen/values/list.rs | 238 ++++ nac3core/src/codegen/values/mod.rs | 28 + nac3core/src/codegen/values/ndarray.rs | 466 +++++++ nac3core/src/codegen/values/range.rs | 153 ++ nac3core/src/toplevel/builtins.rs | 2 +- 21 files changed, 1879 insertions(+), 1796 deletions(-) delete mode 100644 nac3core/src/codegen/classes.rs create mode 100644 nac3core/src/codegen/types/list.rs create mode 100644 nac3core/src/codegen/types/mod.rs create mode 100644 nac3core/src/codegen/types/ndarray.rs create mode 100644 nac3core/src/codegen/types/range.rs create mode 100644 nac3core/src/codegen/values/array.rs create mode 100644 nac3core/src/codegen/values/list.rs create mode 100644 nac3core/src/codegen/values/mod.rs create mode 100644 nac3core/src/codegen/values/ndarray.rs create mode 100644 nac3core/src/codegen/values/range.rs diff --git a/nac3artiq/src/codegen.rs b/nac3artiq/src/codegen.rs index 8bec23ba..85f9b1c1 100644 --- a/nac3artiq/src/codegen.rs +++ b/nac3artiq/src/codegen.rs @@ -14,14 +14,15 @@ use pyo3::{ use nac3core::{ codegen::{ - classes::{ - ArrayLikeIndexer, ArrayLikeValue, ArraySliceValue, ListValue, NDArrayType, - NDArrayValue, ProxyType, ProxyValue, RangeValue, UntypedArrayLikeAccessor, - }, expr::{destructure_range, gen_call}, irrt::call_ndarray_calc_size, llvm_intrinsics::{call_int_smax, call_memcpy_generic, call_stackrestore, call_stacksave}, stmt::{gen_block, gen_for_callback_incrementing, gen_if_callback, gen_with}, + types::{NDArrayType, ProxyType}, + values::{ + ArrayLikeIndexer, ArrayLikeValue, ArraySliceValue, ListValue, NDArrayValue, ProxyValue, + RangeValue, UntypedArrayLikeAccessor, + }, CodeGenContext, CodeGenerator, }, inkwell::{ diff --git a/nac3artiq/src/symbol_resolver.rs b/nac3artiq/src/symbol_resolver.rs index 11662b49..4aeaaf15 100644 --- a/nac3artiq/src/symbol_resolver.rs +++ b/nac3artiq/src/symbol_resolver.rs @@ -14,7 +14,7 @@ use pyo3::{ }; use nac3core::{ - codegen::{classes::NDArrayType, CodeGenContext, CodeGenerator}, + codegen::{types::NDArrayType, CodeGenContext, CodeGenerator}, inkwell::{ module::Linkage, types::{BasicType, BasicTypeEnum}, diff --git a/nac3core/src/codegen/builtin_fns.rs b/nac3core/src/codegen/builtin_fns.rs index 4260ebef..a6749d5c 100644 --- a/nac3core/src/codegen/builtin_fns.rs +++ b/nac3core/src/codegen/builtin_fns.rs @@ -6,10 +6,6 @@ use inkwell::{ use itertools::Itertools; use super::{ - classes::{ - ArrayLikeValue, NDArrayValue, ProxyValue, RangeValue, TypedArrayLikeAccessor, - UntypedArrayLikeAccessor, UntypedArrayLikeMutator, - }, expr::destructure_range, extern_fns, irrt, irrt::calculate_len_for_slice_range, @@ -18,6 +14,10 @@ use super::{ numpy, numpy::ndarray_elementwise_unaryop_impl, stmt::gen_for_callback_incrementing, + values::{ + ArrayLikeValue, NDArrayValue, ProxyValue, RangeValue, TypedArrayLikeAccessor, + UntypedArrayLikeAccessor, UntypedArrayLikeMutator, + }, CodeGenContext, CodeGenerator, }; use crate::{ diff --git a/nac3core/src/codegen/classes.rs b/nac3core/src/codegen/classes.rs deleted file mode 100644 index 6cabcc62..00000000 --- a/nac3core/src/codegen/classes.rs +++ /dev/null @@ -1,1768 +0,0 @@ -use inkwell::{ - context::Context, - types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType}, - values::{BasicValue, BasicValueEnum, IntValue, PointerValue}, - AddressSpace, IntPredicate, -}; - -use super::{ - irrt::{call_ndarray_calc_size, call_ndarray_flatten_index}, - llvm_intrinsics::call_int_umin, - stmt::gen_for_callback_incrementing, - CodeGenContext, CodeGenerator, -}; - -/// A LLVM type that is used to represent a non-primitive type in NAC3. -pub trait ProxyType<'ctx>: Into { - /// The LLVM type of which values of this type possess. This is usually a - /// [LLVM pointer type][PointerType]. - type Base: BasicType<'ctx>; - - /// The type of values represented by this type. - type Value: ProxyValue<'ctx>; - - /// Creates a new value of this type. - fn new_value( - &self, - generator: &mut G, - ctx: &mut CodeGenContext<'ctx, '_>, - name: Option<&'ctx str>, - ) -> Self::Value; - - /// Creates a new array value of this type. - fn new_array_value( - &self, - generator: &mut G, - ctx: &mut CodeGenContext<'ctx, '_>, - size: IntValue<'ctx>, - name: Option<&'ctx str>, - ) -> ArraySliceValue<'ctx>; - - /// Creates a [`value`][ProxyValue] with this as its type. - fn create_value( - &self, - value: >::Base, - name: Option<&'ctx str>, - ) -> Self::Value; - - /// Returns the [base type][Self::Base] of this proxy. - fn as_base_type(&self) -> Self::Base; -} - -/// A LLVM type that is used to represent a non-primitive value in NAC3. -pub trait ProxyValue<'ctx>: Into { - /// The type of LLVM values represented by this instance. This is usually the - /// [LLVM pointer type][PointerValue]. - type Base: BasicValue<'ctx>; - - /// The type of this value. - type Type: ProxyType<'ctx>; - - /// Returns the [type][ProxyType] of this value. - fn get_type(&self) -> Self::Type; - - /// Returns the [base value][Self::Base] of this proxy. - fn as_base_value(&self) -> Self::Base; - - // /// Loads this value into its [underlying representation][Self::Underlying]. Usually involves a - // /// `getelementptr` if [`Self::Base`] is a [pointer value][PointerValue]. - // fn as_underlying_value( - // &self, - // ctx: &mut CodeGenContext<'ctx, '_>, - // name: Option<&'ctx str>, - // ) -> Self::Underlying; -} - -/// An LLVM value that is array-like, i.e. it contains a contiguous, sequenced collection of -/// elements. -pub trait ArrayLikeValue<'ctx> { - /// Returns the element type of this array-like value. - fn element_type( - &self, - ctx: &CodeGenContext<'ctx, '_>, - generator: &G, - ) -> AnyTypeEnum<'ctx>; - - /// Returns the base pointer to the array. - fn base_ptr( - &self, - ctx: &CodeGenContext<'ctx, '_>, - generator: &G, - ) -> PointerValue<'ctx>; - - /// Returns the size of this array-like value. - fn size( - &self, - ctx: &CodeGenContext<'ctx, '_>, - generator: &G, - ) -> IntValue<'ctx>; - - /// Returns a [`ArraySliceValue`] representing this value. - fn as_slice_value( - &self, - ctx: &CodeGenContext<'ctx, '_>, - generator: &G, - ) -> ArraySliceValue<'ctx> { - ArraySliceValue::from_ptr_val( - self.base_ptr(ctx, generator), - self.size(ctx, generator), - None, - ) - } -} - -/// An array-like value that can be indexed by memory offset. -pub trait ArrayLikeIndexer<'ctx, Index = IntValue<'ctx>>: ArrayLikeValue<'ctx> { - /// # Safety - /// - /// This function should be called with a valid index. - unsafe fn ptr_offset_unchecked( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - generator: &mut G, - idx: &Index, - name: Option<&str>, - ) -> PointerValue<'ctx>; - - /// Returns the pointer to the data at the `idx`-th index. - fn ptr_offset( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - generator: &mut G, - idx: &Index, - name: Option<&str>, - ) -> PointerValue<'ctx>; -} - -/// An array-like value that can have its array elements accessed as a [`BasicValueEnum`]. -pub trait UntypedArrayLikeAccessor<'ctx, Index = IntValue<'ctx>>: - ArrayLikeIndexer<'ctx, Index> -{ - /// # Safety - /// - /// This function should be called with a valid index. - unsafe fn get_unchecked( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - generator: &mut G, - idx: &Index, - name: Option<&str>, - ) -> BasicValueEnum<'ctx> { - let ptr = unsafe { self.ptr_offset_unchecked(ctx, generator, idx, name) }; - ctx.builder.build_load(ptr, name.unwrap_or_default()).unwrap() - } - - /// Returns the data at the `idx`-th index. - fn get( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - generator: &mut G, - idx: &Index, - name: Option<&str>, - ) -> BasicValueEnum<'ctx> { - let ptr = self.ptr_offset(ctx, generator, idx, name); - ctx.builder.build_load(ptr, name.unwrap_or_default()).unwrap() - } -} - -/// An array-like value that can have its array elements mutated as a [`BasicValueEnum`]. -pub trait UntypedArrayLikeMutator<'ctx, Index = IntValue<'ctx>>: - ArrayLikeIndexer<'ctx, Index> -{ - /// # Safety - /// - /// This function should be called with a valid index. - unsafe fn set_unchecked( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - generator: &mut G, - idx: &Index, - value: BasicValueEnum<'ctx>, - ) { - let ptr = unsafe { self.ptr_offset_unchecked(ctx, generator, idx, None) }; - ctx.builder.build_store(ptr, value).unwrap(); - } - - /// Sets the data at the `idx`-th index. - fn set( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - generator: &mut G, - idx: &Index, - value: BasicValueEnum<'ctx>, - ) { - let ptr = self.ptr_offset(ctx, generator, idx, None); - ctx.builder.build_store(ptr, value).unwrap(); - } -} - -/// An array-like value that can have its array elements accessed as an arbitrary type `T`. -pub trait TypedArrayLikeAccessor<'ctx, T, Index = IntValue<'ctx>>: - UntypedArrayLikeAccessor<'ctx, Index> -{ - /// Casts an element from [`BasicValueEnum`] into `T`. - fn downcast_to_type( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - value: BasicValueEnum<'ctx>, - ) -> T; - - /// # Safety - /// - /// This function should be called with a valid index. - unsafe fn get_typed_unchecked( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - generator: &mut G, - idx: &Index, - name: Option<&str>, - ) -> T { - let value = unsafe { self.get_unchecked(ctx, generator, idx, name) }; - self.downcast_to_type(ctx, value) - } - - /// Returns the data at the `idx`-th index. - fn get_typed( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - generator: &mut G, - idx: &Index, - name: Option<&str>, - ) -> T { - let value = self.get(ctx, generator, idx, name); - self.downcast_to_type(ctx, value) - } -} - -/// An array-like value that can have its array elements mutated as an arbitrary type `T`. -pub trait TypedArrayLikeMutator<'ctx, T, Index = IntValue<'ctx>>: - UntypedArrayLikeMutator<'ctx, Index> -{ - /// Casts an element from T into [`BasicValueEnum`]. - fn upcast_from_type( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - value: T, - ) -> BasicValueEnum<'ctx>; - - /// # Safety - /// - /// This function should be called with a valid index. - unsafe fn set_typed_unchecked( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - generator: &mut G, - idx: &Index, - value: T, - ) { - let value = self.upcast_from_type(ctx, value); - unsafe { self.set_unchecked(ctx, generator, idx, value) } - } - - /// Sets the data at the `idx`-th index. - fn set_typed( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - generator: &mut G, - idx: &Index, - value: T, - ) { - let value = self.upcast_from_type(ctx, value); - self.set(ctx, generator, idx, value); - } -} - -/// Type alias for a function that casts a [`BasicValueEnum`] into a `T`. -type ValueDowncastFn<'ctx, T> = - Box, BasicValueEnum<'ctx>) -> T>; -/// Type alias for a function that casts a `T` into a [`BasicValueEnum`]. -type ValueUpcastFn<'ctx, T> = Box, T) -> BasicValueEnum<'ctx>>; - -/// An adapter for constraining untyped array values as typed values. -pub struct TypedArrayLikeAdapter<'ctx, T, Adapted: ArrayLikeValue<'ctx> = ArraySliceValue<'ctx>> { - adapted: Adapted, - downcast_fn: ValueDowncastFn<'ctx, T>, - upcast_fn: ValueUpcastFn<'ctx, T>, -} - -impl<'ctx, T, Adapted> TypedArrayLikeAdapter<'ctx, T, Adapted> -where - Adapted: ArrayLikeValue<'ctx>, -{ - /// Creates a [`TypedArrayLikeAdapter`]. - /// - /// * `adapted` - The value to be adapted. - /// * `downcast_fn` - The function converting a [`BasicValueEnum`] into a `T`. - /// * `upcast_fn` - The function converting a T into a [`BasicValueEnum`]. - pub fn from( - adapted: Adapted, - downcast_fn: ValueDowncastFn<'ctx, T>, - upcast_fn: ValueUpcastFn<'ctx, T>, - ) -> Self { - TypedArrayLikeAdapter { adapted, downcast_fn, upcast_fn } - } -} - -impl<'ctx, T, Adapted> ArrayLikeValue<'ctx> for TypedArrayLikeAdapter<'ctx, T, Adapted> -where - Adapted: ArrayLikeValue<'ctx>, -{ - fn element_type( - &self, - ctx: &CodeGenContext<'ctx, '_>, - generator: &G, - ) -> AnyTypeEnum<'ctx> { - self.adapted.element_type(ctx, generator) - } - - fn base_ptr( - &self, - ctx: &CodeGenContext<'ctx, '_>, - generator: &G, - ) -> PointerValue<'ctx> { - self.adapted.base_ptr(ctx, generator) - } - - fn size( - &self, - ctx: &CodeGenContext<'ctx, '_>, - generator: &G, - ) -> IntValue<'ctx> { - self.adapted.size(ctx, generator) - } -} - -impl<'ctx, T, Index, Adapted> ArrayLikeIndexer<'ctx, Index> - for TypedArrayLikeAdapter<'ctx, T, Adapted> -where - Adapted: ArrayLikeIndexer<'ctx, Index>, -{ - unsafe fn ptr_offset_unchecked( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - generator: &mut G, - idx: &Index, - name: Option<&str>, - ) -> PointerValue<'ctx> { - unsafe { self.adapted.ptr_offset_unchecked(ctx, generator, idx, name) } - } - - fn ptr_offset( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - generator: &mut G, - idx: &Index, - name: Option<&str>, - ) -> PointerValue<'ctx> { - self.adapted.ptr_offset(ctx, generator, idx, name) - } -} - -impl<'ctx, T, Index, Adapted> UntypedArrayLikeAccessor<'ctx, Index> - for TypedArrayLikeAdapter<'ctx, T, Adapted> -where - Adapted: UntypedArrayLikeAccessor<'ctx, Index>, -{ -} -impl<'ctx, T, Index, Adapted> UntypedArrayLikeMutator<'ctx, Index> - for TypedArrayLikeAdapter<'ctx, T, Adapted> -where - Adapted: UntypedArrayLikeMutator<'ctx, Index>, -{ -} - -impl<'ctx, T, Index, Adapted> TypedArrayLikeAccessor<'ctx, T, Index> - for TypedArrayLikeAdapter<'ctx, T, Adapted> -where - Adapted: UntypedArrayLikeAccessor<'ctx, Index>, -{ - fn downcast_to_type( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - value: BasicValueEnum<'ctx>, - ) -> T { - (self.downcast_fn)(ctx, value) - } -} - -impl<'ctx, T, Index, Adapted> TypedArrayLikeMutator<'ctx, T, Index> - for TypedArrayLikeAdapter<'ctx, T, Adapted> -where - Adapted: UntypedArrayLikeMutator<'ctx, Index>, -{ - fn upcast_from_type( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - value: T, - ) -> BasicValueEnum<'ctx> { - (self.upcast_fn)(ctx, value) - } -} - -/// An LLVM value representing an array slice, consisting of a pointer to the data and the size of -/// the slice. -#[derive(Copy, Clone)] -pub struct ArraySliceValue<'ctx>(PointerValue<'ctx>, IntValue<'ctx>, Option<&'ctx str>); - -impl<'ctx> ArraySliceValue<'ctx> { - /// Creates an [`ArraySliceValue`] from a [`PointerValue`] and its size. - #[must_use] - pub fn from_ptr_val( - ptr: PointerValue<'ctx>, - size: IntValue<'ctx>, - name: Option<&'ctx str>, - ) -> Self { - ArraySliceValue(ptr, size, name) - } -} - -impl<'ctx> From> for PointerValue<'ctx> { - fn from(value: ArraySliceValue<'ctx>) -> Self { - value.0 - } -} - -impl<'ctx> ArrayLikeValue<'ctx> for ArraySliceValue<'ctx> { - fn element_type( - &self, - _: &CodeGenContext<'ctx, '_>, - _: &G, - ) -> AnyTypeEnum<'ctx> { - self.0.get_type().get_element_type() - } - - fn base_ptr( - &self, - _: &CodeGenContext<'ctx, '_>, - _: &G, - ) -> PointerValue<'ctx> { - self.0 - } - - fn size( - &self, - _: &CodeGenContext<'ctx, '_>, - _: &G, - ) -> IntValue<'ctx> { - self.1 - } -} - -impl<'ctx> ArrayLikeIndexer<'ctx> for ArraySliceValue<'ctx> { - unsafe fn ptr_offset_unchecked( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - generator: &mut G, - idx: &IntValue<'ctx>, - name: Option<&str>, - ) -> PointerValue<'ctx> { - let var_name = name.map(|v| format!("{v}.addr")).unwrap_or_default(); - - unsafe { - ctx.builder - .build_in_bounds_gep(self.base_ptr(ctx, generator), &[*idx], var_name.as_str()) - .unwrap() - } - } - - fn ptr_offset( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - generator: &mut G, - idx: &IntValue<'ctx>, - name: Option<&str>, - ) -> PointerValue<'ctx> { - debug_assert_eq!(idx.get_type(), generator.get_size_type(ctx.ctx)); - - let size = self.size(ctx, generator); - let in_range = ctx.builder.build_int_compare(IntPredicate::ULT, *idx, size, "").unwrap(); - ctx.make_assert( - generator, - in_range, - "0:IndexError", - "list index out of range", - [None, None, None], - ctx.current_loc, - ); - - unsafe { self.ptr_offset_unchecked(ctx, generator, idx, name) } - } -} - -impl<'ctx> UntypedArrayLikeAccessor<'ctx> for ArraySliceValue<'ctx> {} -impl<'ctx> UntypedArrayLikeMutator<'ctx> for ArraySliceValue<'ctx> {} - -/// Proxy type for a `list` type in LLVM. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub struct ListType<'ctx> { - ty: PointerType<'ctx>, - llvm_usize: IntType<'ctx>, -} - -impl<'ctx> ListType<'ctx> { - /// Checks whether `llvm_ty` represents a `list` type, returning [Err] if it does not. - pub fn is_type(llvm_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Result<(), String> { - let llvm_list_ty = llvm_ty.get_element_type(); - let AnyTypeEnum::StructType(llvm_list_ty) = llvm_list_ty else { - return Err(format!("Expected struct type for `list` type, got {llvm_list_ty}")); - }; - if llvm_list_ty.count_fields() != 2 { - return Err(format!( - "Expected 2 fields in `list`, got {}", - llvm_list_ty.count_fields() - )); - } - - let list_size_ty = llvm_list_ty.get_field_type_at_index(0).unwrap(); - let Ok(_) = PointerType::try_from(list_size_ty) else { - return Err(format!("Expected pointer type for `list.0`, got {list_size_ty}")); - }; - - let list_data_ty = llvm_list_ty.get_field_type_at_index(1).unwrap(); - let Ok(list_data_ty) = IntType::try_from(list_data_ty) else { - return Err(format!("Expected int type for `list.1`, got {list_data_ty}")); - }; - if list_data_ty.get_bit_width() != llvm_usize.get_bit_width() { - return Err(format!( - "Expected {}-bit int type for `list.1`, got {}-bit int", - llvm_usize.get_bit_width(), - list_data_ty.get_bit_width() - )); - } - - Ok(()) - } - - /// Creates an instance of [`ListType`]. - #[must_use] - pub fn new( - generator: &G, - ctx: &'ctx Context, - element_type: BasicTypeEnum<'ctx>, - ) -> Self { - let llvm_usize = generator.get_size_type(ctx); - let llvm_list = ctx - .struct_type( - &[element_type.ptr_type(AddressSpace::default()).into(), llvm_usize.into()], - false, - ) - .ptr_type(AddressSpace::default()); - - ListType::from_type(llvm_list, llvm_usize) - } - - /// Creates an [`ListType`] from a [`PointerType`]. - #[must_use] - pub fn from_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self { - debug_assert!(Self::is_type(ptr_ty, llvm_usize).is_ok()); - - ListType { ty: ptr_ty, llvm_usize } - } - - /// Returns the type of the `size` field of this `list` type. - #[must_use] - pub fn size_type(&self) -> IntType<'ctx> { - self.as_base_type() - .get_element_type() - .into_struct_type() - .get_field_type_at_index(1) - .map(BasicTypeEnum::into_int_type) - .unwrap() - } - - /// Returns the element type of this `list` type. - #[must_use] - pub fn element_type(&self) -> AnyTypeEnum<'ctx> { - self.as_base_type() - .get_element_type() - .into_struct_type() - .get_field_type_at_index(0) - .map(BasicTypeEnum::into_pointer_type) - .map(PointerType::get_element_type) - .unwrap() - } -} - -impl<'ctx> ProxyType<'ctx> for ListType<'ctx> { - type Base = PointerType<'ctx>; - type Value = ListValue<'ctx>; - - fn new_value( - &self, - generator: &mut G, - ctx: &mut CodeGenContext<'ctx, '_>, - name: Option<&'ctx str>, - ) -> Self::Value { - self.create_value( - generator - .gen_var_alloc( - ctx, - self.as_base_type().get_element_type().into_struct_type().into(), - name, - ) - .unwrap(), - name, - ) - } - - fn new_array_value( - &self, - generator: &mut G, - ctx: &mut CodeGenContext<'ctx, '_>, - size: IntValue<'ctx>, - name: Option<&'ctx str>, - ) -> ArraySliceValue<'ctx> { - generator - .gen_array_var_alloc( - ctx, - self.as_base_type().get_element_type().into_struct_type().into(), - size, - name, - ) - .unwrap() - } - - fn create_value( - &self, - value: >::Base, - name: Option<&'ctx str>, - ) -> Self::Value { - debug_assert_eq!(value.get_type(), self.as_base_type()); - - ListValue { value, llvm_usize: self.llvm_usize, name } - } - - fn as_base_type(&self) -> Self::Base { - self.ty - } -} - -impl<'ctx> From> for PointerType<'ctx> { - fn from(value: ListType<'ctx>) -> Self { - value.as_base_type() - } -} - -/// Proxy type for accessing a `list` value in LLVM. -#[derive(Copy, Clone)] -pub struct ListValue<'ctx> { - value: PointerValue<'ctx>, - llvm_usize: IntType<'ctx>, - name: Option<&'ctx str>, -} - -impl<'ctx> ListValue<'ctx> { - /// Checks whether `value` is an instance of `list`, returning [Err] if `value` is not an - /// instance. - pub fn is_instance(value: PointerValue<'ctx>, llvm_usize: IntType<'ctx>) -> Result<(), String> { - ListType::is_type(value.get_type(), llvm_usize) - } - - /// Creates an [`ListValue`] from a [`PointerValue`]. - #[must_use] - pub fn from_ptr_val( - ptr: PointerValue<'ctx>, - llvm_usize: IntType<'ctx>, - name: Option<&'ctx str>, - ) -> Self { - debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok()); - - >::Type::from_type(ptr.get_type(), llvm_usize) - .create_value(ptr, name) - } - - /// Returns the double-indirection pointer to the `data` array, as if by calling `getelementptr` - /// on the field. - fn pptr_to_data(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> { - let llvm_i32 = ctx.ctx.i32_type(); - let var_name = self.name.map(|v| format!("{v}.data.addr")).unwrap_or_default(); - - unsafe { - ctx.builder - .build_in_bounds_gep( - self.as_base_value(), - &[llvm_i32.const_zero(), llvm_i32.const_zero()], - var_name.as_str(), - ) - .unwrap() - } - } - - /// Returns the pointer to the field storing the size of this `list`. - fn ptr_to_size(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> { - let llvm_i32 = ctx.ctx.i32_type(); - let var_name = self.name.map(|v| format!("{v}.size.addr")).unwrap_or_default(); - - unsafe { - ctx.builder - .build_in_bounds_gep( - self.as_base_value(), - &[llvm_i32.const_zero(), llvm_i32.const_int(1, true)], - var_name.as_str(), - ) - .unwrap() - } - } - - /// Stores the array of data elements `data` into this instance. - fn store_data(&self, ctx: &CodeGenContext<'ctx, '_>, data: PointerValue<'ctx>) { - ctx.builder.build_store(self.pptr_to_data(ctx), data).unwrap(); - } - - /// Convenience method for creating a new array storing data elements with the given element - /// type `elem_ty` and `size`. - /// - /// If `size` is [None], the size stored in the field of this instance is used instead. - pub fn create_data( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - elem_ty: BasicTypeEnum<'ctx>, - size: Option>, - ) { - let size = size.unwrap_or_else(|| self.load_size(ctx, None)); - - let data = ctx - .builder - .build_select( - ctx.builder - .build_int_compare(IntPredicate::NE, size, self.llvm_usize.const_zero(), "") - .unwrap(), - ctx.builder.build_array_alloca(elem_ty, size, "").unwrap(), - elem_ty.ptr_type(AddressSpace::default()).const_zero(), - "", - ) - .map(BasicValueEnum::into_pointer_value) - .unwrap(); - self.store_data(ctx, data); - } - - /// Returns the double-indirection pointer to the `data` array, as if by calling `getelementptr` - /// on the field. - #[must_use] - pub fn data(&self) -> ListDataProxy<'ctx, '_> { - ListDataProxy(self) - } - - /// Stores the `size` of this `list` into this instance. - pub fn store_size( - &self, - ctx: &CodeGenContext<'ctx, '_>, - generator: &G, - size: IntValue<'ctx>, - ) { - debug_assert_eq!(size.get_type(), generator.get_size_type(ctx.ctx)); - - let psize = self.ptr_to_size(ctx); - ctx.builder.build_store(psize, size).unwrap(); - } - - /// Returns the size of this `list` as a value. - pub fn load_size(&self, ctx: &CodeGenContext<'ctx, '_>, name: Option<&str>) -> IntValue<'ctx> { - let psize = self.ptr_to_size(ctx); - let var_name = name - .map(ToString::to_string) - .or_else(|| self.name.map(|v| format!("{v}.size"))) - .unwrap_or_default(); - - ctx.builder - .build_load(psize, var_name.as_str()) - .map(BasicValueEnum::into_int_value) - .unwrap() - } -} - -impl<'ctx> ProxyValue<'ctx> for ListValue<'ctx> { - type Base = PointerValue<'ctx>; - type Type = ListType<'ctx>; - - fn get_type(&self) -> Self::Type { - ListType::from_type(self.as_base_value().get_type(), self.llvm_usize) - } - - fn as_base_value(&self) -> Self::Base { - self.value - } -} - -impl<'ctx> From> for PointerValue<'ctx> { - fn from(value: ListValue<'ctx>) -> Self { - value.as_base_value() - } -} - -/// Proxy type for accessing the `data` array of an `list` instance in LLVM. -#[derive(Copy, Clone)] -pub struct ListDataProxy<'ctx, 'a>(&'a ListValue<'ctx>); - -impl<'ctx> ArrayLikeValue<'ctx> for ListDataProxy<'ctx, '_> { - fn element_type( - &self, - _: &CodeGenContext<'ctx, '_>, - _: &G, - ) -> AnyTypeEnum<'ctx> { - self.0.value.get_type().get_element_type() - } - - fn base_ptr( - &self, - ctx: &CodeGenContext<'ctx, '_>, - _: &G, - ) -> PointerValue<'ctx> { - let var_name = self.0.name.map(|v| format!("{v}.data")).unwrap_or_default(); - - ctx.builder - .build_load(self.0.pptr_to_data(ctx), var_name.as_str()) - .map(BasicValueEnum::into_pointer_value) - .unwrap() - } - - fn size( - &self, - ctx: &CodeGenContext<'ctx, '_>, - _: &G, - ) -> IntValue<'ctx> { - self.0.load_size(ctx, None) - } -} - -impl<'ctx> ArrayLikeIndexer<'ctx> for ListDataProxy<'ctx, '_> { - unsafe fn ptr_offset_unchecked( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - generator: &mut G, - idx: &IntValue<'ctx>, - name: Option<&str>, - ) -> PointerValue<'ctx> { - let var_name = name.map(|v| format!("{v}.addr")).unwrap_or_default(); - - unsafe { - ctx.builder - .build_in_bounds_gep(self.base_ptr(ctx, generator), &[*idx], var_name.as_str()) - .unwrap() - } - } - - fn ptr_offset( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - generator: &mut G, - idx: &IntValue<'ctx>, - name: Option<&str>, - ) -> PointerValue<'ctx> { - debug_assert_eq!(idx.get_type(), generator.get_size_type(ctx.ctx)); - - let size = self.size(ctx, generator); - let in_range = ctx.builder.build_int_compare(IntPredicate::ULT, *idx, size, "").unwrap(); - ctx.make_assert( - generator, - in_range, - "0:IndexError", - "list index out of range", - [None, None, None], - ctx.current_loc, - ); - - unsafe { self.ptr_offset_unchecked(ctx, generator, idx, name) } - } -} - -impl<'ctx> UntypedArrayLikeAccessor<'ctx> for ListDataProxy<'ctx, '_> {} -impl<'ctx> UntypedArrayLikeMutator<'ctx> for ListDataProxy<'ctx, '_> {} - -/// Proxy type for a `range` type in LLVM. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub struct RangeType<'ctx> { - ty: PointerType<'ctx>, -} - -impl<'ctx> RangeType<'ctx> { - /// Checks whether `llvm_ty` represents a `range` type, returning [Err] if it does not. - pub fn is_type(llvm_ty: PointerType<'ctx>) -> Result<(), String> { - let llvm_range_ty = llvm_ty.get_element_type(); - let AnyTypeEnum::ArrayType(llvm_range_ty) = llvm_range_ty else { - return Err(format!("Expected array type for `range` type, got {llvm_range_ty}")); - }; - if llvm_range_ty.len() != 3 { - return Err(format!( - "Expected 3 elements for `range` type, got {}", - llvm_range_ty.len() - )); - } - - let llvm_range_elem_ty = llvm_range_ty.get_element_type(); - let Ok(llvm_range_elem_ty) = IntType::try_from(llvm_range_elem_ty) else { - return Err(format!( - "Expected int type for `range` element type, got {llvm_range_elem_ty}" - )); - }; - if llvm_range_elem_ty.get_bit_width() != 32 { - return Err(format!( - "Expected 32-bit int type for `range` element type, got {}", - llvm_range_elem_ty.get_bit_width() - )); - } - - Ok(()) - } - - /// Creates an instance of [`RangeType`]. - #[must_use] - pub fn new(ctx: &'ctx Context) -> Self { - let llvm_i32 = ctx.i32_type(); - let llvm_range = llvm_i32.array_type(3).ptr_type(AddressSpace::default()); - - RangeType::from_type(llvm_range) - } - - /// Creates an [`RangeType`] from a [`PointerType`]. - #[must_use] - pub fn from_type(ptr_ty: PointerType<'ctx>) -> Self { - debug_assert!(Self::is_type(ptr_ty).is_ok()); - - RangeType { ty: ptr_ty } - } - - /// Returns the type of all fields of this `range` type. - #[must_use] - pub fn value_type(&self) -> IntType<'ctx> { - self.as_base_type().get_element_type().into_array_type().get_element_type().into_int_type() - } -} - -impl<'ctx> ProxyType<'ctx> for RangeType<'ctx> { - type Base = PointerType<'ctx>; - type Value = RangeValue<'ctx>; - - fn new_value( - &self, - generator: &mut G, - ctx: &mut CodeGenContext<'ctx, '_>, - name: Option<&'ctx str>, - ) -> Self::Value { - self.create_value( - generator - .gen_var_alloc( - ctx, - self.as_base_type().get_element_type().into_struct_type().into(), - name, - ) - .unwrap(), - name, - ) - } - - fn create_value( - &self, - value: >::Base, - name: Option<&'ctx str>, - ) -> Self::Value { - debug_assert_eq!(value.get_type(), self.as_base_type()); - - RangeValue { value, name } - } - - fn new_array_value( - &self, - generator: &mut G, - ctx: &mut CodeGenContext<'ctx, '_>, - size: IntValue<'ctx>, - name: Option<&'ctx str>, - ) -> ArraySliceValue<'ctx> { - generator - .gen_array_var_alloc( - ctx, - self.as_base_type().get_element_type().into_struct_type().into(), - size, - name, - ) - .unwrap() - } - - fn as_base_type(&self) -> Self::Base { - self.ty - } -} - -impl<'ctx> From> for PointerType<'ctx> { - fn from(value: RangeType<'ctx>) -> Self { - value.as_base_type() - } -} - -/// Proxy type for accessing a `range` value in LLVM. -#[derive(Copy, Clone)] -pub struct RangeValue<'ctx> { - value: PointerValue<'ctx>, - name: Option<&'ctx str>, -} - -impl<'ctx> RangeValue<'ctx> { - /// Checks whether `value` is an instance of `range`, returning [Err] if `value` is not an instance. - pub fn is_instance(value: PointerValue<'ctx>) -> Result<(), String> { - RangeType::is_type(value.get_type()) - } - - /// Creates an [`RangeValue`] from a [`PointerValue`]. - #[must_use] - pub fn from_ptr_val(ptr: PointerValue<'ctx>, name: Option<&'ctx str>) -> Self { - debug_assert!(Self::is_instance(ptr).is_ok()); - - >::Type::from_type(ptr.get_type()).create_value(ptr, name) - } - - fn ptr_to_start(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> { - let llvm_i32 = ctx.ctx.i32_type(); - let var_name = self.name.map(|v| format!("{v}.start.addr")).unwrap_or_default(); - - unsafe { - ctx.builder - .build_in_bounds_gep( - self.as_base_value(), - &[llvm_i32.const_zero(), llvm_i32.const_int(0, false)], - var_name.as_str(), - ) - .unwrap() - } - } - - fn ptr_to_end(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> { - let llvm_i32 = ctx.ctx.i32_type(); - let var_name = self.name.map(|v| format!("{v}.end.addr")).unwrap_or_default(); - - unsafe { - ctx.builder - .build_in_bounds_gep( - self.as_base_value(), - &[llvm_i32.const_zero(), llvm_i32.const_int(1, false)], - var_name.as_str(), - ) - .unwrap() - } - } - - fn ptr_to_step(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> { - let llvm_i32 = ctx.ctx.i32_type(); - let var_name = self.name.map(|v| format!("{v}.step.addr")).unwrap_or_default(); - - unsafe { - ctx.builder - .build_in_bounds_gep( - self.as_base_value(), - &[llvm_i32.const_zero(), llvm_i32.const_int(2, false)], - var_name.as_str(), - ) - .unwrap() - } - } - - /// Stores the `start` value into this instance. - pub fn store_start(&self, ctx: &CodeGenContext<'ctx, '_>, start: IntValue<'ctx>) { - debug_assert_eq!(start.get_type().get_bit_width(), 32); - - let pstart = self.ptr_to_start(ctx); - ctx.builder.build_store(pstart, start).unwrap(); - } - - /// Returns the `start` value of this `range`. - pub fn load_start(&self, ctx: &CodeGenContext<'ctx, '_>, name: Option<&str>) -> IntValue<'ctx> { - let pstart = self.ptr_to_start(ctx); - let var_name = name - .map(ToString::to_string) - .or_else(|| self.name.map(|v| format!("{v}.start"))) - .unwrap_or_default(); - - ctx.builder - .build_load(pstart, var_name.as_str()) - .map(BasicValueEnum::into_int_value) - .unwrap() - } - - /// Stores the `end` value into this instance. - pub fn store_end(&self, ctx: &CodeGenContext<'ctx, '_>, end: IntValue<'ctx>) { - debug_assert_eq!(end.get_type().get_bit_width(), 32); - - let pend = self.ptr_to_end(ctx); - ctx.builder.build_store(pend, end).unwrap(); - } - - /// Returns the `end` value of this `range`. - pub fn load_end(&self, ctx: &CodeGenContext<'ctx, '_>, name: Option<&str>) -> IntValue<'ctx> { - let pend = self.ptr_to_end(ctx); - let var_name = name - .map(ToString::to_string) - .or_else(|| self.name.map(|v| format!("{v}.end"))) - .unwrap_or_default(); - - ctx.builder.build_load(pend, var_name.as_str()).map(BasicValueEnum::into_int_value).unwrap() - } - - /// Stores the `step` value into this instance. - pub fn store_step(&self, ctx: &CodeGenContext<'ctx, '_>, step: IntValue<'ctx>) { - debug_assert_eq!(step.get_type().get_bit_width(), 32); - - let pstep = self.ptr_to_step(ctx); - ctx.builder.build_store(pstep, step).unwrap(); - } - - /// Returns the `step` value of this `range`. - pub fn load_step(&self, ctx: &CodeGenContext<'ctx, '_>, name: Option<&str>) -> IntValue<'ctx> { - let pstep = self.ptr_to_step(ctx); - let var_name = name - .map(ToString::to_string) - .or_else(|| self.name.map(|v| format!("{v}.step"))) - .unwrap_or_default(); - - ctx.builder - .build_load(pstep, var_name.as_str()) - .map(BasicValueEnum::into_int_value) - .unwrap() - } -} - -impl<'ctx> ProxyValue<'ctx> for RangeValue<'ctx> { - type Base = PointerValue<'ctx>; - type Type = RangeType<'ctx>; - - fn get_type(&self) -> Self::Type { - RangeType::from_type(self.value.get_type()) - } - - fn as_base_value(&self) -> Self::Base { - self.value - } -} - -impl<'ctx> From> for PointerValue<'ctx> { - fn from(value: RangeValue<'ctx>) -> Self { - value.as_base_value() - } -} - -/// Proxy type for a `ndarray` type in LLVM. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub struct NDArrayType<'ctx> { - ty: PointerType<'ctx>, - llvm_usize: IntType<'ctx>, -} - -impl<'ctx> NDArrayType<'ctx> { - /// Checks whether `llvm_ty` represents a `ndarray` type, returning [Err] if it does not. - pub fn is_type(llvm_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Result<(), String> { - let llvm_ndarray_ty = llvm_ty.get_element_type(); - let AnyTypeEnum::StructType(llvm_ndarray_ty) = llvm_ndarray_ty else { - return Err(format!("Expected struct type for `NDArray` type, got {llvm_ndarray_ty}")); - }; - if llvm_ndarray_ty.count_fields() != 3 { - return Err(format!( - "Expected 3 fields in `NDArray`, got {}", - llvm_ndarray_ty.count_fields() - )); - } - - let ndarray_ndims_ty = llvm_ndarray_ty.get_field_type_at_index(0).unwrap(); - let Ok(ndarray_ndims_ty) = IntType::try_from(ndarray_ndims_ty) else { - return Err(format!("Expected int type for `ndarray.0`, got {ndarray_ndims_ty}")); - }; - if ndarray_ndims_ty.get_bit_width() != llvm_usize.get_bit_width() { - return Err(format!( - "Expected {}-bit int type for `ndarray.0`, got {}-bit int", - llvm_usize.get_bit_width(), - ndarray_ndims_ty.get_bit_width() - )); - } - - let ndarray_dims_ty = llvm_ndarray_ty.get_field_type_at_index(1).unwrap(); - let Ok(ndarray_pdims) = PointerType::try_from(ndarray_dims_ty) else { - return Err(format!("Expected pointer type for `ndarray.1`, got {ndarray_dims_ty}")); - }; - let ndarray_dims = ndarray_pdims.get_element_type(); - let Ok(ndarray_dims) = IntType::try_from(ndarray_dims) else { - return Err(format!( - "Expected pointer-to-int type for `ndarray.1`, got pointer-to-{ndarray_dims}" - )); - }; - if ndarray_dims.get_bit_width() != llvm_usize.get_bit_width() { - return Err(format!( - "Expected pointer-to-{}-bit int type for `ndarray.1`, got pointer-to-{}-bit int", - llvm_usize.get_bit_width(), - ndarray_dims.get_bit_width() - )); - } - - let ndarray_data_ty = llvm_ndarray_ty.get_field_type_at_index(2).unwrap(); - let Ok(_) = PointerType::try_from(ndarray_data_ty) else { - return Err(format!("Expected pointer type for `ndarray.2`, got {ndarray_data_ty}")); - }; - - Ok(()) - } - - /// Creates an instance of [`ListType`]. - #[must_use] - pub fn new( - generator: &G, - ctx: &'ctx Context, - dtype: BasicTypeEnum<'ctx>, - ) -> Self { - let llvm_usize = generator.get_size_type(ctx); - - // struct NDArray { num_dims: size_t, dims: size_t*, data: T* } - // - // * num_dims: Number of dimensions in the array - // * dims: Pointer to an array containing the size of each dimension - // * data: Pointer to an array containing the array data - let llvm_ndarray = ctx - .struct_type( - &[ - llvm_usize.into(), - llvm_usize.ptr_type(AddressSpace::default()).into(), - dtype.ptr_type(AddressSpace::default()).into(), - ], - false, - ) - .ptr_type(AddressSpace::default()); - - NDArrayType::from_type(llvm_ndarray, llvm_usize) - } - - /// Creates an [`NDArrayType`] from a [`PointerType`]. - #[must_use] - pub fn from_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self { - debug_assert!(Self::is_type(ptr_ty, llvm_usize).is_ok()); - - NDArrayType { ty: ptr_ty, llvm_usize } - } - - /// Returns the type of the `size` field of this `ndarray` type. - #[must_use] - pub fn size_type(&self) -> IntType<'ctx> { - self.as_base_type() - .get_element_type() - .into_struct_type() - .get_field_type_at_index(0) - .map(BasicTypeEnum::into_int_type) - .unwrap() - } - - /// Returns the element type of this `ndarray` type. - #[must_use] - pub fn element_type(&self) -> AnyTypeEnum<'ctx> { - self.as_base_type() - .get_element_type() - .into_struct_type() - .get_field_type_at_index(2) - .map(BasicTypeEnum::into_pointer_type) - .map(PointerType::get_element_type) - .unwrap() - } -} - -impl<'ctx> ProxyType<'ctx> for NDArrayType<'ctx> { - type Base = PointerType<'ctx>; - type Value = NDArrayValue<'ctx>; - - fn new_value( - &self, - generator: &mut G, - ctx: &mut CodeGenContext<'ctx, '_>, - name: Option<&'ctx str>, - ) -> Self::Value { - self.create_value( - generator - .gen_var_alloc( - ctx, - self.as_base_type().get_element_type().into_struct_type().into(), - name, - ) - .unwrap(), - name, - ) - } - - fn new_array_value( - &self, - generator: &mut G, - ctx: &mut CodeGenContext<'ctx, '_>, - size: IntValue<'ctx>, - name: Option<&'ctx str>, - ) -> ArraySliceValue<'ctx> { - generator - .gen_array_var_alloc( - ctx, - self.as_base_type().get_element_type().into_struct_type().into(), - size, - name, - ) - .unwrap() - } - - fn create_value( - &self, - value: >::Base, - name: Option<&'ctx str>, - ) -> Self::Value { - debug_assert_eq!(value.get_type(), self.as_base_type()); - - NDArrayValue { value, llvm_usize: self.llvm_usize, name } - } - - fn as_base_type(&self) -> Self::Base { - self.ty - } -} - -impl<'ctx> From> for PointerType<'ctx> { - fn from(value: NDArrayType<'ctx>) -> Self { - value.as_base_type() - } -} - -/// Proxy type for accessing an `NDArray` value in LLVM. -#[derive(Copy, Clone)] -pub struct NDArrayValue<'ctx> { - value: PointerValue<'ctx>, - llvm_usize: IntType<'ctx>, - name: Option<&'ctx str>, -} - -impl<'ctx> NDArrayValue<'ctx> { - /// Checks whether `value` is an instance of `NDArray`, returning [Err] if `value` is not an - /// instance. - pub fn is_instance(value: PointerValue<'ctx>, llvm_usize: IntType<'ctx>) -> Result<(), String> { - NDArrayType::is_type(value.get_type(), llvm_usize) - } - - /// Creates an [`NDArrayValue`] from a [`PointerValue`]. - #[must_use] - pub fn from_ptr_val( - ptr: PointerValue<'ctx>, - llvm_usize: IntType<'ctx>, - name: Option<&'ctx str>, - ) -> Self { - debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok()); - - >::Type::from_type(ptr.get_type(), llvm_usize) - .create_value(ptr, name) - } - - /// Returns the pointer to the field storing the number of dimensions of this `NDArray`. - fn ptr_to_ndims(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> { - let llvm_i32 = ctx.ctx.i32_type(); - let var_name = self.name.map(|v| format!("{v}.ndims.addr")).unwrap_or_default(); - - unsafe { - ctx.builder - .build_in_bounds_gep( - self.as_base_value(), - &[llvm_i32.const_zero(), llvm_i32.const_zero()], - var_name.as_str(), - ) - .unwrap() - } - } - - /// Stores the number of dimensions `ndims` into this instance. - pub fn store_ndims( - &self, - ctx: &CodeGenContext<'ctx, '_>, - generator: &G, - ndims: IntValue<'ctx>, - ) { - debug_assert_eq!(ndims.get_type(), generator.get_size_type(ctx.ctx)); - - let pndims = self.ptr_to_ndims(ctx); - ctx.builder.build_store(pndims, ndims).unwrap(); - } - - /// Returns the number of dimensions of this `NDArray` as a value. - pub fn load_ndims(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { - let pndims = self.ptr_to_ndims(ctx); - ctx.builder.build_load(pndims, "").map(BasicValueEnum::into_int_value).unwrap() - } - - /// Returns the double-indirection pointer to the `dims` array, as if by calling `getelementptr` - /// on the field. - fn ptr_to_dims(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> { - let llvm_i32 = ctx.ctx.i32_type(); - let var_name = self.name.map(|v| format!("{v}.dims.addr")).unwrap_or_default(); - - unsafe { - ctx.builder - .build_in_bounds_gep( - self.as_base_value(), - &[llvm_i32.const_zero(), llvm_i32.const_int(1, true)], - var_name.as_str(), - ) - .unwrap() - } - } - - /// Stores the array of dimension sizes `dims` into this instance. - fn store_dim_sizes(&self, ctx: &CodeGenContext<'ctx, '_>, dims: PointerValue<'ctx>) { - ctx.builder.build_store(self.ptr_to_dims(ctx), dims).unwrap(); - } - - /// Convenience method for creating a new array storing dimension sizes with the given `size`. - pub fn create_dim_sizes( - &self, - ctx: &CodeGenContext<'ctx, '_>, - llvm_usize: IntType<'ctx>, - size: IntValue<'ctx>, - ) { - self.store_dim_sizes(ctx, ctx.builder.build_array_alloca(llvm_usize, size, "").unwrap()); - } - - /// Returns a proxy object to the field storing the size of each dimension of this `NDArray`. - #[must_use] - pub fn dim_sizes(&self) -> NDArrayDimsProxy<'ctx, '_> { - NDArrayDimsProxy(self) - } - - /// Returns the double-indirection pointer to the `data` array, as if by calling `getelementptr` - /// on the field. - pub fn ptr_to_data(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> { - let llvm_i32 = ctx.ctx.i32_type(); - let var_name = self.name.map(|v| format!("{v}.data.addr")).unwrap_or_default(); - - unsafe { - ctx.builder - .build_in_bounds_gep( - self.as_base_value(), - &[llvm_i32.const_zero(), llvm_i32.const_int(2, true)], - var_name.as_str(), - ) - .unwrap() - } - } - - /// Stores the array of data elements `data` into this instance. - fn store_data(&self, ctx: &CodeGenContext<'ctx, '_>, data: PointerValue<'ctx>) { - ctx.builder.build_store(self.ptr_to_data(ctx), data).unwrap(); - } - - /// Convenience method for creating a new array storing data elements with the given element - /// type `elem_ty` and `size`. - pub fn create_data( - &self, - ctx: &CodeGenContext<'ctx, '_>, - elem_ty: BasicTypeEnum<'ctx>, - size: IntValue<'ctx>, - ) { - self.store_data(ctx, ctx.builder.build_array_alloca(elem_ty, size, "").unwrap()); - } - - /// Returns a proxy object to the field storing the data of this `NDArray`. - #[must_use] - pub fn data(&self) -> NDArrayDataProxy<'ctx, '_> { - NDArrayDataProxy(self) - } -} - -impl<'ctx> ProxyValue<'ctx> for NDArrayValue<'ctx> { - type Base = PointerValue<'ctx>; - type Type = NDArrayType<'ctx>; - - fn get_type(&self) -> Self::Type { - NDArrayType::from_type(self.as_base_value().get_type(), self.llvm_usize) - } - - fn as_base_value(&self) -> Self::Base { - self.value - } -} - -impl<'ctx> From> for PointerValue<'ctx> { - fn from(value: NDArrayValue<'ctx>) -> Self { - value.as_base_value() - } -} - -/// Proxy type for accessing the `dims` array of an `NDArray` instance in LLVM. -#[derive(Copy, Clone)] -pub struct NDArrayDimsProxy<'ctx, 'a>(&'a NDArrayValue<'ctx>); - -impl<'ctx> ArrayLikeValue<'ctx> for NDArrayDimsProxy<'ctx, '_> { - fn element_type( - &self, - ctx: &CodeGenContext<'ctx, '_>, - generator: &G, - ) -> AnyTypeEnum<'ctx> { - self.0.dim_sizes().base_ptr(ctx, generator).get_type().get_element_type() - } - - fn base_ptr( - &self, - ctx: &CodeGenContext<'ctx, '_>, - _: &G, - ) -> PointerValue<'ctx> { - let var_name = self.0.name.map(|v| format!("{v}.data")).unwrap_or_default(); - - ctx.builder - .build_load(self.0.ptr_to_dims(ctx), var_name.as_str()) - .map(BasicValueEnum::into_pointer_value) - .unwrap() - } - - fn size( - &self, - ctx: &CodeGenContext<'ctx, '_>, - _: &G, - ) -> IntValue<'ctx> { - self.0.load_ndims(ctx) - } -} - -impl<'ctx> ArrayLikeIndexer<'ctx, IntValue<'ctx>> for NDArrayDimsProxy<'ctx, '_> { - unsafe fn ptr_offset_unchecked( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - generator: &mut G, - idx: &IntValue<'ctx>, - name: Option<&str>, - ) -> PointerValue<'ctx> { - let var_name = name.map(|v| format!("{v}.addr")).unwrap_or_default(); - - unsafe { - ctx.builder - .build_in_bounds_gep(self.base_ptr(ctx, generator), &[*idx], var_name.as_str()) - .unwrap() - } - } - - fn ptr_offset( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - generator: &mut G, - idx: &IntValue<'ctx>, - name: Option<&str>, - ) -> PointerValue<'ctx> { - let size = self.size(ctx, generator); - let in_range = ctx.builder.build_int_compare(IntPredicate::ULT, *idx, size, "").unwrap(); - ctx.make_assert( - generator, - in_range, - "0:IndexError", - "index {0} is out of bounds for axis 0 with size {1}", - [Some(*idx), Some(self.0.load_ndims(ctx)), None], - ctx.current_loc, - ); - - unsafe { self.ptr_offset_unchecked(ctx, generator, idx, name) } - } -} - -impl<'ctx> UntypedArrayLikeAccessor<'ctx, IntValue<'ctx>> for NDArrayDimsProxy<'ctx, '_> {} -impl<'ctx> UntypedArrayLikeMutator<'ctx, IntValue<'ctx>> for NDArrayDimsProxy<'ctx, '_> {} - -impl<'ctx> TypedArrayLikeAccessor<'ctx, IntValue<'ctx>> for NDArrayDimsProxy<'ctx, '_> { - fn downcast_to_type( - &self, - _: &mut CodeGenContext<'ctx, '_>, - value: BasicValueEnum<'ctx>, - ) -> IntValue<'ctx> { - value.into_int_value() - } -} - -impl<'ctx> TypedArrayLikeMutator<'ctx, IntValue<'ctx>> for NDArrayDimsProxy<'ctx, '_> { - fn upcast_from_type( - &self, - _: &mut CodeGenContext<'ctx, '_>, - value: IntValue<'ctx>, - ) -> BasicValueEnum<'ctx> { - value.into() - } -} - -/// Proxy type for accessing the `data` array of an `NDArray` instance in LLVM. -#[derive(Copy, Clone)] -pub struct NDArrayDataProxy<'ctx, 'a>(&'a NDArrayValue<'ctx>); - -impl<'ctx> ArrayLikeValue<'ctx> for NDArrayDataProxy<'ctx, '_> { - fn element_type( - &self, - ctx: &CodeGenContext<'ctx, '_>, - generator: &G, - ) -> AnyTypeEnum<'ctx> { - self.0.data().base_ptr(ctx, generator).get_type().get_element_type() - } - - fn base_ptr( - &self, - ctx: &CodeGenContext<'ctx, '_>, - _: &G, - ) -> PointerValue<'ctx> { - let var_name = self.0.name.map(|v| format!("{v}.data")).unwrap_or_default(); - - ctx.builder - .build_load(self.0.ptr_to_data(ctx), var_name.as_str()) - .map(BasicValueEnum::into_pointer_value) - .unwrap() - } - - fn size( - &self, - ctx: &CodeGenContext<'ctx, '_>, - generator: &G, - ) -> IntValue<'ctx> { - call_ndarray_calc_size(generator, ctx, &self.as_slice_value(ctx, generator), (None, None)) - } -} - -impl<'ctx> ArrayLikeIndexer<'ctx> for NDArrayDataProxy<'ctx, '_> { - unsafe fn ptr_offset_unchecked( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - generator: &mut G, - idx: &IntValue<'ctx>, - name: Option<&str>, - ) -> PointerValue<'ctx> { - unsafe { - ctx.builder - .build_in_bounds_gep( - self.base_ptr(ctx, generator), - &[*idx], - name.unwrap_or_default(), - ) - .unwrap() - } - } - - fn ptr_offset( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - generator: &mut G, - idx: &IntValue<'ctx>, - name: Option<&str>, - ) -> PointerValue<'ctx> { - let data_sz = self.size(ctx, generator); - let in_range = ctx.builder.build_int_compare(IntPredicate::ULT, *idx, data_sz, "").unwrap(); - ctx.make_assert( - generator, - in_range, - "0:IndexError", - "index {0} is out of bounds with size {1}", - [Some(*idx), Some(self.0.load_ndims(ctx)), None], - ctx.current_loc, - ); - - unsafe { self.ptr_offset_unchecked(ctx, generator, idx, name) } - } -} - -impl<'ctx> UntypedArrayLikeAccessor<'ctx, IntValue<'ctx>> for NDArrayDataProxy<'ctx, '_> {} -impl<'ctx> UntypedArrayLikeMutator<'ctx, IntValue<'ctx>> for NDArrayDataProxy<'ctx, '_> {} - -impl<'ctx, Index: UntypedArrayLikeAccessor<'ctx>> ArrayLikeIndexer<'ctx, Index> - for NDArrayDataProxy<'ctx, '_> -{ - unsafe fn ptr_offset_unchecked( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - generator: &mut G, - indices: &Index, - name: Option<&str>, - ) -> PointerValue<'ctx> { - let llvm_usize = generator.get_size_type(ctx.ctx); - - let indices_elem_ty = indices - .ptr_offset(ctx, generator, &llvm_usize.const_zero(), None) - .get_type() - .get_element_type(); - let Ok(indices_elem_ty) = IntType::try_from(indices_elem_ty) else { - panic!("Expected list[int32] but got {indices_elem_ty}") - }; - assert_eq!( - indices_elem_ty.get_bit_width(), - 32, - "Expected list[int32] but got list[int{}]", - indices_elem_ty.get_bit_width() - ); - - let index = call_ndarray_flatten_index(generator, ctx, *self.0, indices); - - unsafe { - ctx.builder - .build_in_bounds_gep( - self.base_ptr(ctx, generator), - &[index], - name.unwrap_or_default(), - ) - .unwrap() - } - } - - fn ptr_offset( - &self, - ctx: &mut CodeGenContext<'ctx, '_>, - generator: &mut G, - indices: &Index, - name: Option<&str>, - ) -> PointerValue<'ctx> { - let llvm_usize = generator.get_size_type(ctx.ctx); - - let indices_size = indices.size(ctx, generator); - let nidx_leq_ndims = ctx - .builder - .build_int_compare(IntPredicate::SLE, indices_size, self.0.load_ndims(ctx), "") - .unwrap(); - ctx.make_assert( - generator, - nidx_leq_ndims, - "0:IndexError", - "invalid index to scalar variable", - [None, None, None], - ctx.current_loc, - ); - - let indices_len = indices.size(ctx, generator); - let ndarray_len = self.0.load_ndims(ctx); - let len = call_int_umin(ctx, indices_len, ndarray_len, None); - gen_for_callback_incrementing( - generator, - ctx, - None, - llvm_usize.const_zero(), - (len, false), - |generator, ctx, _, i| { - let (dim_idx, dim_sz) = unsafe { - ( - indices.get_unchecked(ctx, generator, &i, None).into_int_value(), - self.0.dim_sizes().get_typed_unchecked(ctx, generator, &i, None), - ) - }; - let dim_idx = ctx - .builder - .build_int_z_extend_or_bit_cast(dim_idx, dim_sz.get_type(), "") - .unwrap(); - - let dim_lt = - ctx.builder.build_int_compare(IntPredicate::SLT, dim_idx, dim_sz, "").unwrap(); - - ctx.make_assert( - generator, - dim_lt, - "0:IndexError", - "index {0} is out of bounds for axis 0 with size {1}", - [Some(dim_idx), Some(dim_sz), None], - ctx.current_loc, - ); - - Ok(()) - }, - llvm_usize.const_int(1, false), - ) - .unwrap(); - - unsafe { self.ptr_offset_unchecked(ctx, generator, indices, name) } - } -} - -impl<'ctx, Index: UntypedArrayLikeAccessor<'ctx>> UntypedArrayLikeAccessor<'ctx, Index> - for NDArrayDataProxy<'ctx, '_> -{ -} -impl<'ctx, Index: UntypedArrayLikeAccessor<'ctx>> UntypedArrayLikeMutator<'ctx, Index> - for NDArrayDataProxy<'ctx, '_> -{ -} diff --git a/nac3core/src/codegen/expr.rs b/nac3core/src/codegen/expr.rs index cae650b6..7584826e 100644 --- a/nac3core/src/codegen/expr.rs +++ b/nac3core/src/codegen/expr.rs @@ -19,10 +19,6 @@ use nac3parser::ast::{ }; use super::{ - classes::{ - ArrayLikeIndexer, ArrayLikeValue, ListType, ListValue, NDArrayValue, ProxyType, ProxyValue, - RangeValue, TypedArrayLikeAccessor, UntypedArrayLikeAccessor, - }, concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore}, gen_in_range_check, get_llvm_abi_type, get_llvm_type, get_va_count_arg_name, irrt::*, @@ -36,6 +32,11 @@ use super::{ gen_for_callback_incrementing, gen_if_callback, gen_if_else_expr_callback, gen_raise, gen_var, }, + types::{ListType, ProxyType}, + values::{ + ArrayLikeIndexer, ArrayLikeValue, ListValue, NDArrayValue, ProxyValue, RangeValue, + TypedArrayLikeAccessor, UntypedArrayLikeAccessor, + }, CodeGenContext, CodeGenTask, CodeGenerator, }; use crate::{ diff --git a/nac3core/src/codegen/generator.rs b/nac3core/src/codegen/generator.rs index 5441cbbf..f277ec9a 100644 --- a/nac3core/src/codegen/generator.rs +++ b/nac3core/src/codegen/generator.rs @@ -6,7 +6,7 @@ use inkwell::{ use nac3parser::ast::{Expr, Stmt, StrRef}; -use super::{bool_to_i1, bool_to_i8, classes::ArraySliceValue, expr::*, stmt::*, CodeGenContext}; +use super::{bool_to_i1, bool_to_i8, expr::*, stmt::*, values::ArraySliceValue, CodeGenContext}; use crate::{ symbol_resolver::ValueEnum, toplevel::{DefinitionId, TopLevelDef}, diff --git a/nac3core/src/codegen/irrt/mod.rs b/nac3core/src/codegen/irrt/mod.rs index 15af3cdb..7e70a369 100644 --- a/nac3core/src/codegen/irrt/mod.rs +++ b/nac3core/src/codegen/irrt/mod.rs @@ -12,13 +12,13 @@ use itertools::Either; use nac3parser::ast::Expr; use super::{ - classes::{ - ArrayLikeIndexer, ArrayLikeValue, ArraySliceValue, ListValue, NDArrayValue, - TypedArrayLikeAccessor, TypedArrayLikeAdapter, UntypedArrayLikeAccessor, - }, llvm_intrinsics, macros::codegen_unreachable, stmt::gen_for_callback_incrementing, + values::{ + ArrayLikeIndexer, ArrayLikeValue, ArraySliceValue, ListValue, NDArrayValue, + TypedArrayLikeAccessor, TypedArrayLikeAdapter, UntypedArrayLikeAccessor, + }, CodeGenContext, CodeGenerator, }; use crate::{symbol_resolver::SymbolResolver, typecheck::typedef::Type}; diff --git a/nac3core/src/codegen/mod.rs b/nac3core/src/codegen/mod.rs index 7e99c585..b2bb5ad5 100644 --- a/nac3core/src/codegen/mod.rs +++ b/nac3core/src/codegen/mod.rs @@ -36,12 +36,11 @@ use crate::{ typedef::{CallId, FuncArg, Type, TypeEnum, Unifier}, }, }; -use classes::{ListType, NDArrayType, ProxyType, RangeType}; use concrete_type::{ConcreteType, ConcreteTypeEnum, ConcreteTypeStore}; pub use generator::{CodeGenerator, DefaultCodeGenerator}; +use types::{ListType, NDArrayType, ProxyType, RangeType}; pub mod builtin_fns; -pub mod classes; pub mod concrete_type; pub mod expr; pub mod extern_fns; @@ -50,6 +49,8 @@ pub mod irrt; pub mod llvm_intrinsics; pub mod numpy; pub mod stmt; +pub mod types; +pub mod values; #[cfg(test)] mod test; diff --git a/nac3core/src/codegen/numpy.rs b/nac3core/src/codegen/numpy.rs index ffe0d83d..538809c0 100644 --- a/nac3core/src/codegen/numpy.rs +++ b/nac3core/src/codegen/numpy.rs @@ -7,11 +7,6 @@ use inkwell::{ use nac3parser::ast::{Operator, StrRef}; use super::{ - classes::{ - ArrayLikeIndexer, ArrayLikeValue, ListType, ListValue, NDArrayType, NDArrayValue, - ProxyType, ProxyValue, TypedArrayLikeAccessor, TypedArrayLikeAdapter, - TypedArrayLikeMutator, UntypedArrayLikeAccessor, UntypedArrayLikeMutator, - }, expr::gen_binop_expr_with_values, irrt::{ calculate_len_for_slice_range, call_ndarray_calc_broadcast, @@ -20,6 +15,12 @@ use super::{ llvm_intrinsics::{self, call_memcpy_generic}, macros::codegen_unreachable, stmt::{gen_for_callback_incrementing, gen_for_range_callback, gen_if_else_expr_callback}, + types::{ListType, NDArrayType, ProxyType}, + values::{ + ArrayLikeIndexer, ArrayLikeValue, ListValue, NDArrayValue, ProxyValue, + TypedArrayLikeAccessor, TypedArrayLikeAdapter, TypedArrayLikeMutator, + UntypedArrayLikeAccessor, UntypedArrayLikeMutator, + }, CodeGenContext, CodeGenerator, }; use crate::{ diff --git a/nac3core/src/codegen/stmt.rs b/nac3core/src/codegen/stmt.rs index cfc188c8..d008f7bd 100644 --- a/nac3core/src/codegen/stmt.rs +++ b/nac3core/src/codegen/stmt.rs @@ -12,11 +12,11 @@ use nac3parser::ast::{ }; use super::{ - classes::{ArrayLikeIndexer, ArraySliceValue, ListValue, RangeValue}, expr::{destructure_range, gen_binop_expr}, gen_in_range_check, irrt::{handle_slice_indices, list_slice_assignment}, macros::codegen_unreachable, + values::{ArrayLikeIndexer, ArraySliceValue, ListValue, RangeValue}, CodeGenContext, CodeGenerator, }; use crate::{ diff --git a/nac3core/src/codegen/test.rs b/nac3core/src/codegen/test.rs index 2bd02a71..5dd60706 100644 --- a/nac3core/src/codegen/test.rs +++ b/nac3core/src/codegen/test.rs @@ -16,8 +16,8 @@ use nac3parser::{ use parking_lot::RwLock; use super::{ - classes::{ListType, NDArrayType, ProxyType, RangeType}, concrete_type::ConcreteTypeStore, + types::{ListType, NDArrayType, ProxyType, RangeType}, CodeGenContext, CodeGenLLVMOptions, CodeGenTargetMachineOptions, CodeGenTask, CodeGenerator, DefaultCodeGenerator, WithCall, WorkerRegistry, }; diff --git a/nac3core/src/codegen/types/list.rs b/nac3core/src/codegen/types/list.rs new file mode 100644 index 00000000..c68e4abf --- /dev/null +++ b/nac3core/src/codegen/types/list.rs @@ -0,0 +1,163 @@ +use inkwell::{ + context::Context, + types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType}, + values::IntValue, + AddressSpace, +}; + +use super::ProxyType; +use crate::codegen::{ + values::{ArraySliceValue, ListValue, ProxyValue}, + CodeGenContext, CodeGenerator, +}; + +/// Proxy type for a `list` type in LLVM. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct ListType<'ctx> { + ty: PointerType<'ctx>, + llvm_usize: IntType<'ctx>, +} + +impl<'ctx> ListType<'ctx> { + /// Checks whether `llvm_ty` represents a `list` type, returning [Err] if it does not. + pub fn is_type(llvm_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Result<(), String> { + let llvm_list_ty = llvm_ty.get_element_type(); + let AnyTypeEnum::StructType(llvm_list_ty) = llvm_list_ty else { + return Err(format!("Expected struct type for `list` type, got {llvm_list_ty}")); + }; + if llvm_list_ty.count_fields() != 2 { + return Err(format!( + "Expected 2 fields in `list`, got {}", + llvm_list_ty.count_fields() + )); + } + + let list_size_ty = llvm_list_ty.get_field_type_at_index(0).unwrap(); + let Ok(_) = PointerType::try_from(list_size_ty) else { + return Err(format!("Expected pointer type for `list.0`, got {list_size_ty}")); + }; + + let list_data_ty = llvm_list_ty.get_field_type_at_index(1).unwrap(); + let Ok(list_data_ty) = IntType::try_from(list_data_ty) else { + return Err(format!("Expected int type for `list.1`, got {list_data_ty}")); + }; + if list_data_ty.get_bit_width() != llvm_usize.get_bit_width() { + return Err(format!( + "Expected {}-bit int type for `list.1`, got {}-bit int", + llvm_usize.get_bit_width(), + list_data_ty.get_bit_width() + )); + } + + Ok(()) + } + + /// Creates an instance of [`ListType`]. + #[must_use] + pub fn new( + generator: &G, + ctx: &'ctx Context, + element_type: BasicTypeEnum<'ctx>, + ) -> Self { + let llvm_usize = generator.get_size_type(ctx); + let llvm_list = ctx + .struct_type( + &[element_type.ptr_type(AddressSpace::default()).into(), llvm_usize.into()], + false, + ) + .ptr_type(AddressSpace::default()); + + ListType::from_type(llvm_list, llvm_usize) + } + + /// Creates an [`ListType`] from a [`PointerType`]. + #[must_use] + pub fn from_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self { + debug_assert!(Self::is_type(ptr_ty, llvm_usize).is_ok()); + + ListType { ty: ptr_ty, llvm_usize } + } + + /// Returns the type of the `size` field of this `list` type. + #[must_use] + pub fn size_type(&self) -> IntType<'ctx> { + self.as_base_type() + .get_element_type() + .into_struct_type() + .get_field_type_at_index(1) + .map(BasicTypeEnum::into_int_type) + .unwrap() + } + + /// Returns the element type of this `list` type. + #[must_use] + pub fn element_type(&self) -> AnyTypeEnum<'ctx> { + self.as_base_type() + .get_element_type() + .into_struct_type() + .get_field_type_at_index(0) + .map(BasicTypeEnum::into_pointer_type) + .map(PointerType::get_element_type) + .unwrap() + } +} + +impl<'ctx> ProxyType<'ctx> for ListType<'ctx> { + type Base = PointerType<'ctx>; + type Value = ListValue<'ctx>; + + fn new_value( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + name: Option<&'ctx str>, + ) -> Self::Value { + self.map_value( + generator + .gen_var_alloc( + ctx, + self.as_base_type().get_element_type().into_struct_type().into(), + name, + ) + .unwrap(), + name, + ) + } + + fn new_array_value( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + size: IntValue<'ctx>, + name: Option<&'ctx str>, + ) -> ArraySliceValue<'ctx> { + generator + .gen_array_var_alloc( + ctx, + self.as_base_type().get_element_type().into_struct_type().into(), + size, + name, + ) + .unwrap() + } + + fn map_value( + &self, + value: >::Base, + name: Option<&'ctx str>, + ) -> Self::Value { + debug_assert_eq!(value.get_type(), self.as_base_type()); + + ListValue::from_ptr_val(value, self.llvm_usize, name) + } + + fn as_base_type(&self) -> Self::Base { + self.ty + } +} + +impl<'ctx> From> for PointerType<'ctx> { + fn from(value: ListType<'ctx>) -> Self { + value.as_base_type() + } +} diff --git a/nac3core/src/codegen/types/mod.rs b/nac3core/src/codegen/types/mod.rs new file mode 100644 index 00000000..6032936b --- /dev/null +++ b/nac3core/src/codegen/types/mod.rs @@ -0,0 +1,50 @@ +use inkwell::{types::BasicType, values::IntValue}; + +use super::{ + values::{ArraySliceValue, ProxyValue}, + {CodeGenContext, CodeGenerator}, +}; +pub use list::*; +pub use ndarray::*; +pub use range::*; + +mod list; +mod ndarray; +mod range; + +/// A LLVM type that is used to represent a corresponding type in NAC3. +pub trait ProxyType<'ctx>: Into { + /// The LLVM type of which values of this type possess. This is usually a + /// [LLVM pointer type][PointerType] for any non-primitive types. + type Base: BasicType<'ctx>; + + /// The type of values represented by this type. + type Value: ProxyValue<'ctx>; + + /// Creates a new value of this type. + fn new_value( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + name: Option<&'ctx str>, + ) -> Self::Value; + + /// Creates a new array value of this type. + fn new_array_value( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + size: IntValue<'ctx>, + name: Option<&'ctx str>, + ) -> ArraySliceValue<'ctx>; + + /// Converts an existing value into a [`ProxyValue`] of this type. + fn map_value( + &self, + value: >::Base, + name: Option<&'ctx str>, + ) -> Self::Value; + + /// Returns the [base type][Self::Base] of this proxy. + fn as_base_type(&self) -> Self::Base; +} diff --git a/nac3core/src/codegen/types/ndarray.rs b/nac3core/src/codegen/types/ndarray.rs new file mode 100644 index 00000000..780a7a6d --- /dev/null +++ b/nac3core/src/codegen/types/ndarray.rs @@ -0,0 +1,191 @@ +use inkwell::{ + context::Context, + types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType}, + values::IntValue, + AddressSpace, +}; + +use super::ProxyType; +use crate::codegen::{ + values::{ArraySliceValue, NDArrayValue, ProxyValue}, + {CodeGenContext, CodeGenerator}, +}; + +/// Proxy type for a `ndarray` type in LLVM. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct NDArrayType<'ctx> { + ty: PointerType<'ctx>, + llvm_usize: IntType<'ctx>, +} + +impl<'ctx> NDArrayType<'ctx> { + /// Checks whether `llvm_ty` represents a `ndarray` type, returning [Err] if it does not. + pub fn is_type(llvm_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Result<(), String> { + let llvm_ndarray_ty = llvm_ty.get_element_type(); + let AnyTypeEnum::StructType(llvm_ndarray_ty) = llvm_ndarray_ty else { + return Err(format!("Expected struct type for `NDArray` type, got {llvm_ndarray_ty}")); + }; + if llvm_ndarray_ty.count_fields() != 3 { + return Err(format!( + "Expected 3 fields in `NDArray`, got {}", + llvm_ndarray_ty.count_fields() + )); + } + + let ndarray_ndims_ty = llvm_ndarray_ty.get_field_type_at_index(0).unwrap(); + let Ok(ndarray_ndims_ty) = IntType::try_from(ndarray_ndims_ty) else { + return Err(format!("Expected int type for `ndarray.0`, got {ndarray_ndims_ty}")); + }; + if ndarray_ndims_ty.get_bit_width() != llvm_usize.get_bit_width() { + return Err(format!( + "Expected {}-bit int type for `ndarray.0`, got {}-bit int", + llvm_usize.get_bit_width(), + ndarray_ndims_ty.get_bit_width() + )); + } + + let ndarray_dims_ty = llvm_ndarray_ty.get_field_type_at_index(1).unwrap(); + let Ok(ndarray_pdims) = PointerType::try_from(ndarray_dims_ty) else { + return Err(format!("Expected pointer type for `ndarray.1`, got {ndarray_dims_ty}")); + }; + let ndarray_dims = ndarray_pdims.get_element_type(); + let Ok(ndarray_dims) = IntType::try_from(ndarray_dims) else { + return Err(format!( + "Expected pointer-to-int type for `ndarray.1`, got pointer-to-{ndarray_dims}" + )); + }; + if ndarray_dims.get_bit_width() != llvm_usize.get_bit_width() { + return Err(format!( + "Expected pointer-to-{}-bit int type for `ndarray.1`, got pointer-to-{}-bit int", + llvm_usize.get_bit_width(), + ndarray_dims.get_bit_width() + )); + } + + let ndarray_data_ty = llvm_ndarray_ty.get_field_type_at_index(2).unwrap(); + let Ok(_) = PointerType::try_from(ndarray_data_ty) else { + return Err(format!("Expected pointer type for `ndarray.2`, got {ndarray_data_ty}")); + }; + + Ok(()) + } + + /// Creates an instance of [`ListType`]. + #[must_use] + pub fn new( + generator: &G, + ctx: &'ctx Context, + dtype: BasicTypeEnum<'ctx>, + ) -> Self { + let llvm_usize = generator.get_size_type(ctx); + + // struct NDArray { num_dims: size_t, dims: size_t*, data: T* } + // + // * num_dims: Number of dimensions in the array + // * dims: Pointer to an array containing the size of each dimension + // * data: Pointer to an array containing the array data + let llvm_ndarray = ctx + .struct_type( + &[ + llvm_usize.into(), + llvm_usize.ptr_type(AddressSpace::default()).into(), + dtype.ptr_type(AddressSpace::default()).into(), + ], + false, + ) + .ptr_type(AddressSpace::default()); + + NDArrayType::from_type(llvm_ndarray, llvm_usize) + } + + /// Creates an [`NDArrayType`] from a [`PointerType`]. + #[must_use] + pub fn from_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self { + debug_assert!(Self::is_type(ptr_ty, llvm_usize).is_ok()); + + NDArrayType { ty: ptr_ty, llvm_usize } + } + + /// Returns the type of the `size` field of this `ndarray` type. + #[must_use] + pub fn size_type(&self) -> IntType<'ctx> { + self.as_base_type() + .get_element_type() + .into_struct_type() + .get_field_type_at_index(0) + .map(BasicTypeEnum::into_int_type) + .unwrap() + } + + /// Returns the element type of this `ndarray` type. + #[must_use] + pub fn element_type(&self) -> AnyTypeEnum<'ctx> { + self.as_base_type() + .get_element_type() + .into_struct_type() + .get_field_type_at_index(2) + .map(BasicTypeEnum::into_pointer_type) + .map(PointerType::get_element_type) + .unwrap() + } +} + +impl<'ctx> ProxyType<'ctx> for NDArrayType<'ctx> { + type Base = PointerType<'ctx>; + type Value = NDArrayValue<'ctx>; + + fn new_value( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + name: Option<&'ctx str>, + ) -> Self::Value { + self.map_value( + generator + .gen_var_alloc( + ctx, + self.as_base_type().get_element_type().into_struct_type().into(), + name, + ) + .unwrap(), + name, + ) + } + + fn new_array_value( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + size: IntValue<'ctx>, + name: Option<&'ctx str>, + ) -> ArraySliceValue<'ctx> { + generator + .gen_array_var_alloc( + ctx, + self.as_base_type().get_element_type().into_struct_type().into(), + size, + name, + ) + .unwrap() + } + + fn map_value( + &self, + value: >::Base, + name: Option<&'ctx str>, + ) -> Self::Value { + debug_assert_eq!(value.get_type(), self.as_base_type()); + + NDArrayValue::from_ptr_val(value, self.llvm_usize, name) + } + + fn as_base_type(&self) -> Self::Base { + self.ty + } +} + +impl<'ctx> From> for PointerType<'ctx> { + fn from(value: NDArrayType<'ctx>) -> Self { + value.as_base_type() + } +} diff --git a/nac3core/src/codegen/types/range.rs b/nac3core/src/codegen/types/range.rs new file mode 100644 index 00000000..3b22916d --- /dev/null +++ b/nac3core/src/codegen/types/range.rs @@ -0,0 +1,132 @@ +use inkwell::{ + context::Context, + types::{AnyTypeEnum, IntType, PointerType}, + values::IntValue, + AddressSpace, +}; + +use super::ProxyType; +use crate::codegen::{ + values::{ArraySliceValue, ProxyValue, RangeValue}, + {CodeGenContext, CodeGenerator}, +}; + +/// Proxy type for a `range` type in LLVM. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct RangeType<'ctx> { + ty: PointerType<'ctx>, +} + +impl<'ctx> RangeType<'ctx> { + /// Checks whether `llvm_ty` represents a `range` type, returning [Err] if it does not. + pub fn is_type(llvm_ty: PointerType<'ctx>) -> Result<(), String> { + let llvm_range_ty = llvm_ty.get_element_type(); + let AnyTypeEnum::ArrayType(llvm_range_ty) = llvm_range_ty else { + return Err(format!("Expected array type for `range` type, got {llvm_range_ty}")); + }; + if llvm_range_ty.len() != 3 { + return Err(format!( + "Expected 3 elements for `range` type, got {}", + llvm_range_ty.len() + )); + } + + let llvm_range_elem_ty = llvm_range_ty.get_element_type(); + let Ok(llvm_range_elem_ty) = IntType::try_from(llvm_range_elem_ty) else { + return Err(format!( + "Expected int type for `range` element type, got {llvm_range_elem_ty}" + )); + }; + if llvm_range_elem_ty.get_bit_width() != 32 { + return Err(format!( + "Expected 32-bit int type for `range` element type, got {}", + llvm_range_elem_ty.get_bit_width() + )); + } + + Ok(()) + } + + /// Creates an instance of [`RangeType`]. + #[must_use] + pub fn new(ctx: &'ctx Context) -> Self { + let llvm_i32 = ctx.i32_type(); + let llvm_range = llvm_i32.array_type(3).ptr_type(AddressSpace::default()); + + RangeType::from_type(llvm_range) + } + + /// Creates an [`RangeType`] from a [`PointerType`]. + #[must_use] + pub fn from_type(ptr_ty: PointerType<'ctx>) -> Self { + debug_assert!(Self::is_type(ptr_ty).is_ok()); + + RangeType { ty: ptr_ty } + } + + /// Returns the type of all fields of this `range` type. + #[must_use] + pub fn value_type(&self) -> IntType<'ctx> { + self.as_base_type().get_element_type().into_array_type().get_element_type().into_int_type() + } +} + +impl<'ctx> ProxyType<'ctx> for RangeType<'ctx> { + type Base = PointerType<'ctx>; + type Value = RangeValue<'ctx>; + + fn new_value( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + name: Option<&'ctx str>, + ) -> Self::Value { + self.map_value( + generator + .gen_var_alloc( + ctx, + self.as_base_type().get_element_type().into_struct_type().into(), + name, + ) + .unwrap(), + name, + ) + } + + fn new_array_value( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + size: IntValue<'ctx>, + name: Option<&'ctx str>, + ) -> ArraySliceValue<'ctx> { + generator + .gen_array_var_alloc( + ctx, + self.as_base_type().get_element_type().into_struct_type().into(), + size, + name, + ) + .unwrap() + } + + fn map_value( + &self, + value: >::Base, + name: Option<&'ctx str>, + ) -> Self::Value { + debug_assert_eq!(value.get_type(), self.as_base_type()); + + RangeValue::from_ptr_val(value, name) + } + + fn as_base_type(&self) -> Self::Base { + self.ty + } +} + +impl<'ctx> From> for PointerType<'ctx> { + fn from(value: RangeType<'ctx>) -> Self { + value.as_base_type() + } +} diff --git a/nac3core/src/codegen/values/array.rs b/nac3core/src/codegen/values/array.rs new file mode 100644 index 00000000..8d14fe8a --- /dev/null +++ b/nac3core/src/codegen/values/array.rs @@ -0,0 +1,426 @@ +use inkwell::{ + types::AnyTypeEnum, + values::{BasicValueEnum, IntValue, PointerValue}, + IntPredicate, +}; + +use crate::codegen::{CodeGenContext, CodeGenerator}; + +/// An LLVM value that is array-like, i.e. it contains a contiguous, sequenced collection of +/// elements. +pub trait ArrayLikeValue<'ctx> { + /// Returns the element type of this array-like value. + fn element_type( + &self, + ctx: &CodeGenContext<'ctx, '_>, + generator: &G, + ) -> AnyTypeEnum<'ctx>; + + /// Returns the base pointer to the array. + fn base_ptr( + &self, + ctx: &CodeGenContext<'ctx, '_>, + generator: &G, + ) -> PointerValue<'ctx>; + + /// Returns the size of this array-like value. + fn size( + &self, + ctx: &CodeGenContext<'ctx, '_>, + generator: &G, + ) -> IntValue<'ctx>; + + /// Returns a [`ArraySliceValue`] representing this value. + fn as_slice_value( + &self, + ctx: &CodeGenContext<'ctx, '_>, + generator: &G, + ) -> ArraySliceValue<'ctx> { + ArraySliceValue::from_ptr_val( + self.base_ptr(ctx, generator), + self.size(ctx, generator), + None, + ) + } +} + +/// An array-like value that can be indexed by memory offset. +pub trait ArrayLikeIndexer<'ctx, Index = IntValue<'ctx>>: ArrayLikeValue<'ctx> { + /// # Safety + /// + /// This function should be called with a valid index. + unsafe fn ptr_offset_unchecked( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + generator: &mut G, + idx: &Index, + name: Option<&str>, + ) -> PointerValue<'ctx>; + + /// Returns the pointer to the data at the `idx`-th index. + fn ptr_offset( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + generator: &mut G, + idx: &Index, + name: Option<&str>, + ) -> PointerValue<'ctx>; +} + +/// An array-like value that can have its array elements accessed as a [`BasicValueEnum`]. +pub trait UntypedArrayLikeAccessor<'ctx, Index = IntValue<'ctx>>: + ArrayLikeIndexer<'ctx, Index> +{ + /// # Safety + /// + /// This function should be called with a valid index. + unsafe fn get_unchecked( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + generator: &mut G, + idx: &Index, + name: Option<&str>, + ) -> BasicValueEnum<'ctx> { + let ptr = unsafe { self.ptr_offset_unchecked(ctx, generator, idx, name) }; + ctx.builder.build_load(ptr, name.unwrap_or_default()).unwrap() + } + + /// Returns the data at the `idx`-th index. + fn get( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + generator: &mut G, + idx: &Index, + name: Option<&str>, + ) -> BasicValueEnum<'ctx> { + let ptr = self.ptr_offset(ctx, generator, idx, name); + ctx.builder.build_load(ptr, name.unwrap_or_default()).unwrap() + } +} + +/// An array-like value that can have its array elements mutated as a [`BasicValueEnum`]. +pub trait UntypedArrayLikeMutator<'ctx, Index = IntValue<'ctx>>: + ArrayLikeIndexer<'ctx, Index> +{ + /// # Safety + /// + /// This function should be called with a valid index. + unsafe fn set_unchecked( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + generator: &mut G, + idx: &Index, + value: BasicValueEnum<'ctx>, + ) { + let ptr = unsafe { self.ptr_offset_unchecked(ctx, generator, idx, None) }; + ctx.builder.build_store(ptr, value).unwrap(); + } + + /// Sets the data at the `idx`-th index. + fn set( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + generator: &mut G, + idx: &Index, + value: BasicValueEnum<'ctx>, + ) { + let ptr = self.ptr_offset(ctx, generator, idx, None); + ctx.builder.build_store(ptr, value).unwrap(); + } +} + +/// An array-like value that can have its array elements accessed as an arbitrary type `T`. +pub trait TypedArrayLikeAccessor<'ctx, T, Index = IntValue<'ctx>>: + UntypedArrayLikeAccessor<'ctx, Index> +{ + /// Casts an element from [`BasicValueEnum`] into `T`. + fn downcast_to_type( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + value: BasicValueEnum<'ctx>, + ) -> T; + + /// # Safety + /// + /// This function should be called with a valid index. + unsafe fn get_typed_unchecked( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + generator: &mut G, + idx: &Index, + name: Option<&str>, + ) -> T { + let value = unsafe { self.get_unchecked(ctx, generator, idx, name) }; + self.downcast_to_type(ctx, value) + } + + /// Returns the data at the `idx`-th index. + fn get_typed( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + generator: &mut G, + idx: &Index, + name: Option<&str>, + ) -> T { + let value = self.get(ctx, generator, idx, name); + self.downcast_to_type(ctx, value) + } +} + +/// An array-like value that can have its array elements mutated as an arbitrary type `T`. +pub trait TypedArrayLikeMutator<'ctx, T, Index = IntValue<'ctx>>: + UntypedArrayLikeMutator<'ctx, Index> +{ + /// Casts an element from T into [`BasicValueEnum`]. + fn upcast_from_type( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + value: T, + ) -> BasicValueEnum<'ctx>; + + /// # Safety + /// + /// This function should be called with a valid index. + unsafe fn set_typed_unchecked( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + generator: &mut G, + idx: &Index, + value: T, + ) { + let value = self.upcast_from_type(ctx, value); + unsafe { self.set_unchecked(ctx, generator, idx, value) } + } + + /// Sets the data at the `idx`-th index. + fn set_typed( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + generator: &mut G, + idx: &Index, + value: T, + ) { + let value = self.upcast_from_type(ctx, value); + self.set(ctx, generator, idx, value); + } +} + +/// Type alias for a function that casts a [`BasicValueEnum`] into a `T`. +type ValueDowncastFn<'ctx, T> = + Box, BasicValueEnum<'ctx>) -> T>; +/// Type alias for a function that casts a `T` into a [`BasicValueEnum`]. +type ValueUpcastFn<'ctx, T> = Box, T) -> BasicValueEnum<'ctx>>; + +/// An adapter for constraining untyped array values as typed values. +pub struct TypedArrayLikeAdapter<'ctx, T, Adapted: ArrayLikeValue<'ctx> = ArraySliceValue<'ctx>> { + adapted: Adapted, + downcast_fn: ValueDowncastFn<'ctx, T>, + upcast_fn: ValueUpcastFn<'ctx, T>, +} + +impl<'ctx, T, Adapted> TypedArrayLikeAdapter<'ctx, T, Adapted> +where + Adapted: ArrayLikeValue<'ctx>, +{ + /// Creates a [`TypedArrayLikeAdapter`]. + /// + /// * `adapted` - The value to be adapted. + /// * `downcast_fn` - The function converting a [`BasicValueEnum`] into a `T`. + /// * `upcast_fn` - The function converting a T into a [`BasicValueEnum`]. + pub fn from( + adapted: Adapted, + downcast_fn: ValueDowncastFn<'ctx, T>, + upcast_fn: ValueUpcastFn<'ctx, T>, + ) -> Self { + TypedArrayLikeAdapter { adapted, downcast_fn, upcast_fn } + } +} + +impl<'ctx, T, Adapted> ArrayLikeValue<'ctx> for TypedArrayLikeAdapter<'ctx, T, Adapted> +where + Adapted: ArrayLikeValue<'ctx>, +{ + fn element_type( + &self, + ctx: &CodeGenContext<'ctx, '_>, + generator: &G, + ) -> AnyTypeEnum<'ctx> { + self.adapted.element_type(ctx, generator) + } + + fn base_ptr( + &self, + ctx: &CodeGenContext<'ctx, '_>, + generator: &G, + ) -> PointerValue<'ctx> { + self.adapted.base_ptr(ctx, generator) + } + + fn size( + &self, + ctx: &CodeGenContext<'ctx, '_>, + generator: &G, + ) -> IntValue<'ctx> { + self.adapted.size(ctx, generator) + } +} + +impl<'ctx, T, Index, Adapted> ArrayLikeIndexer<'ctx, Index> + for TypedArrayLikeAdapter<'ctx, T, Adapted> +where + Adapted: ArrayLikeIndexer<'ctx, Index>, +{ + unsafe fn ptr_offset_unchecked( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + generator: &mut G, + idx: &Index, + name: Option<&str>, + ) -> PointerValue<'ctx> { + unsafe { self.adapted.ptr_offset_unchecked(ctx, generator, idx, name) } + } + + fn ptr_offset( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + generator: &mut G, + idx: &Index, + name: Option<&str>, + ) -> PointerValue<'ctx> { + self.adapted.ptr_offset(ctx, generator, idx, name) + } +} + +impl<'ctx, T, Index, Adapted> UntypedArrayLikeAccessor<'ctx, Index> + for TypedArrayLikeAdapter<'ctx, T, Adapted> +where + Adapted: UntypedArrayLikeAccessor<'ctx, Index>, +{ +} +impl<'ctx, T, Index, Adapted> UntypedArrayLikeMutator<'ctx, Index> + for TypedArrayLikeAdapter<'ctx, T, Adapted> +where + Adapted: UntypedArrayLikeMutator<'ctx, Index>, +{ +} + +impl<'ctx, T, Index, Adapted> TypedArrayLikeAccessor<'ctx, T, Index> + for TypedArrayLikeAdapter<'ctx, T, Adapted> +where + Adapted: UntypedArrayLikeAccessor<'ctx, Index>, +{ + fn downcast_to_type( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + value: BasicValueEnum<'ctx>, + ) -> T { + (self.downcast_fn)(ctx, value) + } +} + +impl<'ctx, T, Index, Adapted> TypedArrayLikeMutator<'ctx, T, Index> + for TypedArrayLikeAdapter<'ctx, T, Adapted> +where + Adapted: UntypedArrayLikeMutator<'ctx, Index>, +{ + fn upcast_from_type( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + value: T, + ) -> BasicValueEnum<'ctx> { + (self.upcast_fn)(ctx, value) + } +} + +/// An LLVM value representing an array slice, consisting of a pointer to the data and the size of +/// the slice. +#[derive(Copy, Clone)] +pub struct ArraySliceValue<'ctx>(PointerValue<'ctx>, IntValue<'ctx>, Option<&'ctx str>); + +impl<'ctx> ArraySliceValue<'ctx> { + /// Creates an [`ArraySliceValue`] from a [`PointerValue`] and its size. + #[must_use] + pub fn from_ptr_val( + ptr: PointerValue<'ctx>, + size: IntValue<'ctx>, + name: Option<&'ctx str>, + ) -> Self { + ArraySliceValue(ptr, size, name) + } +} + +impl<'ctx> From> for PointerValue<'ctx> { + fn from(value: ArraySliceValue<'ctx>) -> Self { + value.0 + } +} + +impl<'ctx> ArrayLikeValue<'ctx> for ArraySliceValue<'ctx> { + fn element_type( + &self, + _: &CodeGenContext<'ctx, '_>, + _: &G, + ) -> AnyTypeEnum<'ctx> { + self.0.get_type().get_element_type() + } + + fn base_ptr( + &self, + _: &CodeGenContext<'ctx, '_>, + _: &G, + ) -> PointerValue<'ctx> { + self.0 + } + + fn size( + &self, + _: &CodeGenContext<'ctx, '_>, + _: &G, + ) -> IntValue<'ctx> { + self.1 + } +} + +impl<'ctx> ArrayLikeIndexer<'ctx> for ArraySliceValue<'ctx> { + unsafe fn ptr_offset_unchecked( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + generator: &mut G, + idx: &IntValue<'ctx>, + name: Option<&str>, + ) -> PointerValue<'ctx> { + let var_name = name.map(|v| format!("{v}.addr")).unwrap_or_default(); + + unsafe { + ctx.builder + .build_in_bounds_gep(self.base_ptr(ctx, generator), &[*idx], var_name.as_str()) + .unwrap() + } + } + + fn ptr_offset( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + generator: &mut G, + idx: &IntValue<'ctx>, + name: Option<&str>, + ) -> PointerValue<'ctx> { + debug_assert_eq!(idx.get_type(), generator.get_size_type(ctx.ctx)); + + let size = self.size(ctx, generator); + let in_range = ctx.builder.build_int_compare(IntPredicate::ULT, *idx, size, "").unwrap(); + ctx.make_assert( + generator, + in_range, + "0:IndexError", + "list index out of range", + [None, None, None], + ctx.current_loc, + ); + + unsafe { self.ptr_offset_unchecked(ctx, generator, idx, name) } + } +} + +impl<'ctx> UntypedArrayLikeAccessor<'ctx> for ArraySliceValue<'ctx> {} +impl<'ctx> UntypedArrayLikeMutator<'ctx> for ArraySliceValue<'ctx> {} diff --git a/nac3core/src/codegen/values/list.rs b/nac3core/src/codegen/values/list.rs new file mode 100644 index 00000000..e81993d4 --- /dev/null +++ b/nac3core/src/codegen/values/list.rs @@ -0,0 +1,238 @@ +use inkwell::{ + types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType}, + values::{BasicValueEnum, IntValue, PointerValue}, + AddressSpace, IntPredicate, +}; + +use super::{ + ArrayLikeIndexer, ArrayLikeValue, ProxyValue, UntypedArrayLikeAccessor, UntypedArrayLikeMutator, +}; +use crate::codegen::{ + types::ListType, + {CodeGenContext, CodeGenerator}, +}; + +/// Proxy type for accessing a `list` value in LLVM. +#[derive(Copy, Clone)] +pub struct ListValue<'ctx> { + value: PointerValue<'ctx>, + llvm_usize: IntType<'ctx>, + name: Option<&'ctx str>, +} + +impl<'ctx> ListValue<'ctx> { + /// Checks whether `value` is an instance of `list`, returning [Err] if `value` is not an + /// instance. + pub fn is_instance(value: PointerValue<'ctx>, llvm_usize: IntType<'ctx>) -> Result<(), String> { + ListType::is_type(value.get_type(), llvm_usize) + } + + /// Creates an [`ListValue`] from a [`PointerValue`]. + #[must_use] + pub fn from_ptr_val( + ptr: PointerValue<'ctx>, + llvm_usize: IntType<'ctx>, + name: Option<&'ctx str>, + ) -> Self { + debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok()); + + ListValue { value: ptr, llvm_usize, name } + } + + /// Returns the double-indirection pointer to the `data` array, as if by calling `getelementptr` + /// on the field. + fn pptr_to_data(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> { + let llvm_i32 = ctx.ctx.i32_type(); + let var_name = self.name.map(|v| format!("{v}.data.addr")).unwrap_or_default(); + + unsafe { + ctx.builder + .build_in_bounds_gep( + self.as_base_value(), + &[llvm_i32.const_zero(), llvm_i32.const_zero()], + var_name.as_str(), + ) + .unwrap() + } + } + + /// Returns the pointer to the field storing the size of this `list`. + fn ptr_to_size(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> { + let llvm_i32 = ctx.ctx.i32_type(); + let var_name = self.name.map(|v| format!("{v}.size.addr")).unwrap_or_default(); + + unsafe { + ctx.builder + .build_in_bounds_gep( + self.as_base_value(), + &[llvm_i32.const_zero(), llvm_i32.const_int(1, true)], + var_name.as_str(), + ) + .unwrap() + } + } + + /// Stores the array of data elements `data` into this instance. + fn store_data(&self, ctx: &CodeGenContext<'ctx, '_>, data: PointerValue<'ctx>) { + ctx.builder.build_store(self.pptr_to_data(ctx), data).unwrap(); + } + + /// Convenience method for creating a new array storing data elements with the given element + /// type `elem_ty` and `size`. + /// + /// If `size` is [None], the size stored in the field of this instance is used instead. + pub fn create_data( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + elem_ty: BasicTypeEnum<'ctx>, + size: Option>, + ) { + let size = size.unwrap_or_else(|| self.load_size(ctx, None)); + + let data = ctx + .builder + .build_select( + ctx.builder + .build_int_compare(IntPredicate::NE, size, self.llvm_usize.const_zero(), "") + .unwrap(), + ctx.builder.build_array_alloca(elem_ty, size, "").unwrap(), + elem_ty.ptr_type(AddressSpace::default()).const_zero(), + "", + ) + .map(BasicValueEnum::into_pointer_value) + .unwrap(); + self.store_data(ctx, data); + } + + /// Returns the double-indirection pointer to the `data` array, as if by calling `getelementptr` + /// on the field. + #[must_use] + pub fn data(&self) -> ListDataProxy<'ctx, '_> { + ListDataProxy(self) + } + + /// Stores the `size` of this `list` into this instance. + pub fn store_size( + &self, + ctx: &CodeGenContext<'ctx, '_>, + generator: &G, + size: IntValue<'ctx>, + ) { + debug_assert_eq!(size.get_type(), generator.get_size_type(ctx.ctx)); + + let psize = self.ptr_to_size(ctx); + ctx.builder.build_store(psize, size).unwrap(); + } + + /// Returns the size of this `list` as a value. + pub fn load_size(&self, ctx: &CodeGenContext<'ctx, '_>, name: Option<&str>) -> IntValue<'ctx> { + let psize = self.ptr_to_size(ctx); + let var_name = name + .map(ToString::to_string) + .or_else(|| self.name.map(|v| format!("{v}.size"))) + .unwrap_or_default(); + + ctx.builder + .build_load(psize, var_name.as_str()) + .map(BasicValueEnum::into_int_value) + .unwrap() + } +} + +impl<'ctx> ProxyValue<'ctx> for ListValue<'ctx> { + type Base = PointerValue<'ctx>; + type Type = ListType<'ctx>; + + fn get_type(&self) -> Self::Type { + ListType::from_type(self.as_base_value().get_type(), self.llvm_usize) + } + + fn as_base_value(&self) -> Self::Base { + self.value + } +} + +impl<'ctx> From> for PointerValue<'ctx> { + fn from(value: ListValue<'ctx>) -> Self { + value.as_base_value() + } +} + +/// Proxy type for accessing the `data` array of an `list` instance in LLVM. +#[derive(Copy, Clone)] +pub struct ListDataProxy<'ctx, 'a>(&'a ListValue<'ctx>); + +impl<'ctx> ArrayLikeValue<'ctx> for ListDataProxy<'ctx, '_> { + fn element_type( + &self, + _: &CodeGenContext<'ctx, '_>, + _: &G, + ) -> AnyTypeEnum<'ctx> { + self.0.value.get_type().get_element_type() + } + + fn base_ptr( + &self, + ctx: &CodeGenContext<'ctx, '_>, + _: &G, + ) -> PointerValue<'ctx> { + let var_name = self.0.name.map(|v| format!("{v}.data")).unwrap_or_default(); + + ctx.builder + .build_load(self.0.pptr_to_data(ctx), var_name.as_str()) + .map(BasicValueEnum::into_pointer_value) + .unwrap() + } + + fn size( + &self, + ctx: &CodeGenContext<'ctx, '_>, + _: &G, + ) -> IntValue<'ctx> { + self.0.load_size(ctx, None) + } +} + +impl<'ctx> ArrayLikeIndexer<'ctx> for ListDataProxy<'ctx, '_> { + unsafe fn ptr_offset_unchecked( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + generator: &mut G, + idx: &IntValue<'ctx>, + name: Option<&str>, + ) -> PointerValue<'ctx> { + let var_name = name.map(|v| format!("{v}.addr")).unwrap_or_default(); + + unsafe { + ctx.builder + .build_in_bounds_gep(self.base_ptr(ctx, generator), &[*idx], var_name.as_str()) + .unwrap() + } + } + + fn ptr_offset( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + generator: &mut G, + idx: &IntValue<'ctx>, + name: Option<&str>, + ) -> PointerValue<'ctx> { + debug_assert_eq!(idx.get_type(), generator.get_size_type(ctx.ctx)); + + let size = self.size(ctx, generator); + let in_range = ctx.builder.build_int_compare(IntPredicate::ULT, *idx, size, "").unwrap(); + ctx.make_assert( + generator, + in_range, + "0:IndexError", + "list index out of range", + [None, None, None], + ctx.current_loc, + ); + + unsafe { self.ptr_offset_unchecked(ctx, generator, idx, name) } + } +} + +impl<'ctx> UntypedArrayLikeAccessor<'ctx> for ListDataProxy<'ctx, '_> {} +impl<'ctx> UntypedArrayLikeMutator<'ctx> for ListDataProxy<'ctx, '_> {} diff --git a/nac3core/src/codegen/values/mod.rs b/nac3core/src/codegen/values/mod.rs new file mode 100644 index 00000000..db1c7bf9 --- /dev/null +++ b/nac3core/src/codegen/values/mod.rs @@ -0,0 +1,28 @@ +use inkwell::values::BasicValue; + +use super::types::ProxyType; +pub use array::*; +pub use list::*; +pub use ndarray::*; +pub use range::*; + +mod array; +mod list; +mod ndarray; +mod range; + +/// A LLVM type that is used to represent a non-primitive value in NAC3. +pub trait ProxyValue<'ctx>: Into { + /// The type of LLVM values represented by this instance. This is usually the + /// [LLVM pointer type][PointerValue]. + type Base: BasicValue<'ctx>; + + /// The type of this value. + type Type: ProxyType<'ctx>; + + /// Returns the [type][ProxyType] of this value. + fn get_type(&self) -> Self::Type; + + /// Returns the [base value][Self::Base] of this proxy. + fn as_base_value(&self) -> Self::Base; +} diff --git a/nac3core/src/codegen/values/ndarray.rs b/nac3core/src/codegen/values/ndarray.rs new file mode 100644 index 00000000..23e88365 --- /dev/null +++ b/nac3core/src/codegen/values/ndarray.rs @@ -0,0 +1,466 @@ +use inkwell::{ + types::{AnyTypeEnum, BasicTypeEnum, IntType}, + values::{BasicValueEnum, IntValue, PointerValue}, + IntPredicate, +}; + +use super::{ + ArrayLikeIndexer, ArrayLikeValue, ProxyValue, TypedArrayLikeAccessor, TypedArrayLikeMutator, + UntypedArrayLikeAccessor, UntypedArrayLikeMutator, +}; +use crate::codegen::{ + irrt::{call_ndarray_calc_size, call_ndarray_flatten_index}, + llvm_intrinsics::call_int_umin, + stmt::gen_for_callback_incrementing, + types::NDArrayType, + CodeGenContext, CodeGenerator, +}; + +/// Proxy type for accessing an `NDArray` value in LLVM. +#[derive(Copy, Clone)] +pub struct NDArrayValue<'ctx> { + value: PointerValue<'ctx>, + llvm_usize: IntType<'ctx>, + name: Option<&'ctx str>, +} + +impl<'ctx> NDArrayValue<'ctx> { + /// Checks whether `value` is an instance of `NDArray`, returning [Err] if `value` is not an + /// instance. + pub fn is_instance(value: PointerValue<'ctx>, llvm_usize: IntType<'ctx>) -> Result<(), String> { + NDArrayType::is_type(value.get_type(), llvm_usize) + } + + /// Creates an [`NDArrayValue`] from a [`PointerValue`]. + #[must_use] + pub fn from_ptr_val( + ptr: PointerValue<'ctx>, + llvm_usize: IntType<'ctx>, + name: Option<&'ctx str>, + ) -> Self { + debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok()); + + NDArrayValue { value: ptr, llvm_usize, name } + } + + /// Returns the pointer to the field storing the number of dimensions of this `NDArray`. + fn ptr_to_ndims(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> { + let llvm_i32 = ctx.ctx.i32_type(); + let var_name = self.name.map(|v| format!("{v}.ndims.addr")).unwrap_or_default(); + + unsafe { + ctx.builder + .build_in_bounds_gep( + self.as_base_value(), + &[llvm_i32.const_zero(), llvm_i32.const_zero()], + var_name.as_str(), + ) + .unwrap() + } + } + + /// Stores the number of dimensions `ndims` into this instance. + pub fn store_ndims( + &self, + ctx: &CodeGenContext<'ctx, '_>, + generator: &G, + ndims: IntValue<'ctx>, + ) { + debug_assert_eq!(ndims.get_type(), generator.get_size_type(ctx.ctx)); + + let pndims = self.ptr_to_ndims(ctx); + ctx.builder.build_store(pndims, ndims).unwrap(); + } + + /// Returns the number of dimensions of this `NDArray` as a value. + pub fn load_ndims(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { + let pndims = self.ptr_to_ndims(ctx); + ctx.builder.build_load(pndims, "").map(BasicValueEnum::into_int_value).unwrap() + } + + /// Returns the double-indirection pointer to the `dims` array, as if by calling `getelementptr` + /// on the field. + fn ptr_to_dims(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> { + let llvm_i32 = ctx.ctx.i32_type(); + let var_name = self.name.map(|v| format!("{v}.dims.addr")).unwrap_or_default(); + + unsafe { + ctx.builder + .build_in_bounds_gep( + self.as_base_value(), + &[llvm_i32.const_zero(), llvm_i32.const_int(1, true)], + var_name.as_str(), + ) + .unwrap() + } + } + + /// Stores the array of dimension sizes `dims` into this instance. + fn store_dim_sizes(&self, ctx: &CodeGenContext<'ctx, '_>, dims: PointerValue<'ctx>) { + ctx.builder.build_store(self.ptr_to_dims(ctx), dims).unwrap(); + } + + /// Convenience method for creating a new array storing dimension sizes with the given `size`. + pub fn create_dim_sizes( + &self, + ctx: &CodeGenContext<'ctx, '_>, + llvm_usize: IntType<'ctx>, + size: IntValue<'ctx>, + ) { + self.store_dim_sizes(ctx, ctx.builder.build_array_alloca(llvm_usize, size, "").unwrap()); + } + + /// Returns a proxy object to the field storing the size of each dimension of this `NDArray`. + #[must_use] + pub fn dim_sizes(&self) -> NDArrayDimsProxy<'ctx, '_> { + NDArrayDimsProxy(self) + } + + /// Returns the double-indirection pointer to the `data` array, as if by calling `getelementptr` + /// on the field. + pub fn ptr_to_data(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> { + let llvm_i32 = ctx.ctx.i32_type(); + let var_name = self.name.map(|v| format!("{v}.data.addr")).unwrap_or_default(); + + unsafe { + ctx.builder + .build_in_bounds_gep( + self.as_base_value(), + &[llvm_i32.const_zero(), llvm_i32.const_int(2, true)], + var_name.as_str(), + ) + .unwrap() + } + } + + /// Stores the array of data elements `data` into this instance. + fn store_data(&self, ctx: &CodeGenContext<'ctx, '_>, data: PointerValue<'ctx>) { + ctx.builder.build_store(self.ptr_to_data(ctx), data).unwrap(); + } + + /// Convenience method for creating a new array storing data elements with the given element + /// type `elem_ty` and `size`. + pub fn create_data( + &self, + ctx: &CodeGenContext<'ctx, '_>, + elem_ty: BasicTypeEnum<'ctx>, + size: IntValue<'ctx>, + ) { + self.store_data(ctx, ctx.builder.build_array_alloca(elem_ty, size, "").unwrap()); + } + + /// Returns a proxy object to the field storing the data of this `NDArray`. + #[must_use] + pub fn data(&self) -> NDArrayDataProxy<'ctx, '_> { + NDArrayDataProxy(self) + } +} + +impl<'ctx> ProxyValue<'ctx> for NDArrayValue<'ctx> { + type Base = PointerValue<'ctx>; + type Type = NDArrayType<'ctx>; + + fn get_type(&self) -> Self::Type { + NDArrayType::from_type(self.as_base_value().get_type(), self.llvm_usize) + } + + fn as_base_value(&self) -> Self::Base { + self.value + } +} + +impl<'ctx> From> for PointerValue<'ctx> { + fn from(value: NDArrayValue<'ctx>) -> Self { + value.as_base_value() + } +} + +/// Proxy type for accessing the `dims` array of an `NDArray` instance in LLVM. +#[derive(Copy, Clone)] +pub struct NDArrayDimsProxy<'ctx, 'a>(&'a NDArrayValue<'ctx>); + +impl<'ctx> ArrayLikeValue<'ctx> for NDArrayDimsProxy<'ctx, '_> { + fn element_type( + &self, + ctx: &CodeGenContext<'ctx, '_>, + generator: &G, + ) -> AnyTypeEnum<'ctx> { + self.0.dim_sizes().base_ptr(ctx, generator).get_type().get_element_type() + } + + fn base_ptr( + &self, + ctx: &CodeGenContext<'ctx, '_>, + _: &G, + ) -> PointerValue<'ctx> { + let var_name = self.0.name.map(|v| format!("{v}.data")).unwrap_or_default(); + + ctx.builder + .build_load(self.0.ptr_to_dims(ctx), var_name.as_str()) + .map(BasicValueEnum::into_pointer_value) + .unwrap() + } + + fn size( + &self, + ctx: &CodeGenContext<'ctx, '_>, + _: &G, + ) -> IntValue<'ctx> { + self.0.load_ndims(ctx) + } +} + +impl<'ctx> ArrayLikeIndexer<'ctx, IntValue<'ctx>> for NDArrayDimsProxy<'ctx, '_> { + unsafe fn ptr_offset_unchecked( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + generator: &mut G, + idx: &IntValue<'ctx>, + name: Option<&str>, + ) -> PointerValue<'ctx> { + let var_name = name.map(|v| format!("{v}.addr")).unwrap_or_default(); + + unsafe { + ctx.builder + .build_in_bounds_gep(self.base_ptr(ctx, generator), &[*idx], var_name.as_str()) + .unwrap() + } + } + + fn ptr_offset( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + generator: &mut G, + idx: &IntValue<'ctx>, + name: Option<&str>, + ) -> PointerValue<'ctx> { + let size = self.size(ctx, generator); + let in_range = ctx.builder.build_int_compare(IntPredicate::ULT, *idx, size, "").unwrap(); + ctx.make_assert( + generator, + in_range, + "0:IndexError", + "index {0} is out of bounds for axis 0 with size {1}", + [Some(*idx), Some(self.0.load_ndims(ctx)), None], + ctx.current_loc, + ); + + unsafe { self.ptr_offset_unchecked(ctx, generator, idx, name) } + } +} + +impl<'ctx> UntypedArrayLikeAccessor<'ctx, IntValue<'ctx>> for NDArrayDimsProxy<'ctx, '_> {} +impl<'ctx> UntypedArrayLikeMutator<'ctx, IntValue<'ctx>> for NDArrayDimsProxy<'ctx, '_> {} + +impl<'ctx> TypedArrayLikeAccessor<'ctx, IntValue<'ctx>> for NDArrayDimsProxy<'ctx, '_> { + fn downcast_to_type( + &self, + _: &mut CodeGenContext<'ctx, '_>, + value: BasicValueEnum<'ctx>, + ) -> IntValue<'ctx> { + value.into_int_value() + } +} + +impl<'ctx> TypedArrayLikeMutator<'ctx, IntValue<'ctx>> for NDArrayDimsProxy<'ctx, '_> { + fn upcast_from_type( + &self, + _: &mut CodeGenContext<'ctx, '_>, + value: IntValue<'ctx>, + ) -> BasicValueEnum<'ctx> { + value.into() + } +} + +/// Proxy type for accessing the `data` array of an `NDArray` instance in LLVM. +#[derive(Copy, Clone)] +pub struct NDArrayDataProxy<'ctx, 'a>(&'a NDArrayValue<'ctx>); + +impl<'ctx> ArrayLikeValue<'ctx> for NDArrayDataProxy<'ctx, '_> { + fn element_type( + &self, + ctx: &CodeGenContext<'ctx, '_>, + generator: &G, + ) -> AnyTypeEnum<'ctx> { + self.0.data().base_ptr(ctx, generator).get_type().get_element_type() + } + + fn base_ptr( + &self, + ctx: &CodeGenContext<'ctx, '_>, + _: &G, + ) -> PointerValue<'ctx> { + let var_name = self.0.name.map(|v| format!("{v}.data")).unwrap_or_default(); + + ctx.builder + .build_load(self.0.ptr_to_data(ctx), var_name.as_str()) + .map(BasicValueEnum::into_pointer_value) + .unwrap() + } + + fn size( + &self, + ctx: &CodeGenContext<'ctx, '_>, + generator: &G, + ) -> IntValue<'ctx> { + call_ndarray_calc_size(generator, ctx, &self.as_slice_value(ctx, generator), (None, None)) + } +} + +impl<'ctx> ArrayLikeIndexer<'ctx> for NDArrayDataProxy<'ctx, '_> { + unsafe fn ptr_offset_unchecked( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + generator: &mut G, + idx: &IntValue<'ctx>, + name: Option<&str>, + ) -> PointerValue<'ctx> { + unsafe { + ctx.builder + .build_in_bounds_gep( + self.base_ptr(ctx, generator), + &[*idx], + name.unwrap_or_default(), + ) + .unwrap() + } + } + + fn ptr_offset( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + generator: &mut G, + idx: &IntValue<'ctx>, + name: Option<&str>, + ) -> PointerValue<'ctx> { + let data_sz = self.size(ctx, generator); + let in_range = ctx.builder.build_int_compare(IntPredicate::ULT, *idx, data_sz, "").unwrap(); + ctx.make_assert( + generator, + in_range, + "0:IndexError", + "index {0} is out of bounds with size {1}", + [Some(*idx), Some(self.0.load_ndims(ctx)), None], + ctx.current_loc, + ); + + unsafe { self.ptr_offset_unchecked(ctx, generator, idx, name) } + } +} + +impl<'ctx> UntypedArrayLikeAccessor<'ctx, IntValue<'ctx>> for NDArrayDataProxy<'ctx, '_> {} +impl<'ctx> UntypedArrayLikeMutator<'ctx, IntValue<'ctx>> for NDArrayDataProxy<'ctx, '_> {} + +impl<'ctx, Index: UntypedArrayLikeAccessor<'ctx>> ArrayLikeIndexer<'ctx, Index> + for NDArrayDataProxy<'ctx, '_> +{ + unsafe fn ptr_offset_unchecked( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + generator: &mut G, + indices: &Index, + name: Option<&str>, + ) -> PointerValue<'ctx> { + let llvm_usize = generator.get_size_type(ctx.ctx); + + let indices_elem_ty = indices + .ptr_offset(ctx, generator, &llvm_usize.const_zero(), None) + .get_type() + .get_element_type(); + let Ok(indices_elem_ty) = IntType::try_from(indices_elem_ty) else { + panic!("Expected list[int32] but got {indices_elem_ty}") + }; + assert_eq!( + indices_elem_ty.get_bit_width(), + 32, + "Expected list[int32] but got list[int{}]", + indices_elem_ty.get_bit_width() + ); + + let index = call_ndarray_flatten_index(generator, ctx, *self.0, indices); + + unsafe { + ctx.builder + .build_in_bounds_gep( + self.base_ptr(ctx, generator), + &[index], + name.unwrap_or_default(), + ) + .unwrap() + } + } + + fn ptr_offset( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + generator: &mut G, + indices: &Index, + name: Option<&str>, + ) -> PointerValue<'ctx> { + let llvm_usize = generator.get_size_type(ctx.ctx); + + let indices_size = indices.size(ctx, generator); + let nidx_leq_ndims = ctx + .builder + .build_int_compare(IntPredicate::SLE, indices_size, self.0.load_ndims(ctx), "") + .unwrap(); + ctx.make_assert( + generator, + nidx_leq_ndims, + "0:IndexError", + "invalid index to scalar variable", + [None, None, None], + ctx.current_loc, + ); + + let indices_len = indices.size(ctx, generator); + let ndarray_len = self.0.load_ndims(ctx); + let len = call_int_umin(ctx, indices_len, ndarray_len, None); + gen_for_callback_incrementing( + generator, + ctx, + None, + llvm_usize.const_zero(), + (len, false), + |generator, ctx, _, i| { + let (dim_idx, dim_sz) = unsafe { + ( + indices.get_unchecked(ctx, generator, &i, None).into_int_value(), + self.0.dim_sizes().get_typed_unchecked(ctx, generator, &i, None), + ) + }; + let dim_idx = ctx + .builder + .build_int_z_extend_or_bit_cast(dim_idx, dim_sz.get_type(), "") + .unwrap(); + + let dim_lt = + ctx.builder.build_int_compare(IntPredicate::SLT, dim_idx, dim_sz, "").unwrap(); + + ctx.make_assert( + generator, + dim_lt, + "0:IndexError", + "index {0} is out of bounds for axis 0 with size {1}", + [Some(dim_idx), Some(dim_sz), None], + ctx.current_loc, + ); + + Ok(()) + }, + llvm_usize.const_int(1, false), + ) + .unwrap(); + + unsafe { self.ptr_offset_unchecked(ctx, generator, indices, name) } + } +} + +impl<'ctx, Index: UntypedArrayLikeAccessor<'ctx>> UntypedArrayLikeAccessor<'ctx, Index> + for NDArrayDataProxy<'ctx, '_> +{ +} +impl<'ctx, Index: UntypedArrayLikeAccessor<'ctx>> UntypedArrayLikeMutator<'ctx, Index> + for NDArrayDataProxy<'ctx, '_> +{ +} diff --git a/nac3core/src/codegen/values/range.rs b/nac3core/src/codegen/values/range.rs new file mode 100644 index 00000000..40deb35e --- /dev/null +++ b/nac3core/src/codegen/values/range.rs @@ -0,0 +1,153 @@ +use inkwell::values::{BasicValueEnum, IntValue, PointerValue}; + +use super::ProxyValue; +use crate::codegen::{types::RangeType, CodeGenContext}; + +/// Proxy type for accessing a `range` value in LLVM. +#[derive(Copy, Clone)] +pub struct RangeValue<'ctx> { + value: PointerValue<'ctx>, + name: Option<&'ctx str>, +} + +impl<'ctx> RangeValue<'ctx> { + /// Checks whether `value` is an instance of `range`, returning [Err] if `value` is not an instance. + pub fn is_instance(value: PointerValue<'ctx>) -> Result<(), String> { + RangeType::is_type(value.get_type()) + } + + /// Creates an [`RangeValue`] from a [`PointerValue`]. + #[must_use] + pub fn from_ptr_val(ptr: PointerValue<'ctx>, name: Option<&'ctx str>) -> Self { + debug_assert!(Self::is_instance(ptr).is_ok()); + + RangeValue { value: ptr, name } + } + + fn ptr_to_start(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> { + let llvm_i32 = ctx.ctx.i32_type(); + let var_name = self.name.map(|v| format!("{v}.start.addr")).unwrap_or_default(); + + unsafe { + ctx.builder + .build_in_bounds_gep( + self.as_base_value(), + &[llvm_i32.const_zero(), llvm_i32.const_int(0, false)], + var_name.as_str(), + ) + .unwrap() + } + } + + fn ptr_to_end(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> { + let llvm_i32 = ctx.ctx.i32_type(); + let var_name = self.name.map(|v| format!("{v}.end.addr")).unwrap_or_default(); + + unsafe { + ctx.builder + .build_in_bounds_gep( + self.as_base_value(), + &[llvm_i32.const_zero(), llvm_i32.const_int(1, false)], + var_name.as_str(), + ) + .unwrap() + } + } + + fn ptr_to_step(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> { + let llvm_i32 = ctx.ctx.i32_type(); + let var_name = self.name.map(|v| format!("{v}.step.addr")).unwrap_or_default(); + + unsafe { + ctx.builder + .build_in_bounds_gep( + self.as_base_value(), + &[llvm_i32.const_zero(), llvm_i32.const_int(2, false)], + var_name.as_str(), + ) + .unwrap() + } + } + + /// Stores the `start` value into this instance. + pub fn store_start(&self, ctx: &CodeGenContext<'ctx, '_>, start: IntValue<'ctx>) { + debug_assert_eq!(start.get_type().get_bit_width(), 32); + + let pstart = self.ptr_to_start(ctx); + ctx.builder.build_store(pstart, start).unwrap(); + } + + /// Returns the `start` value of this `range`. + pub fn load_start(&self, ctx: &CodeGenContext<'ctx, '_>, name: Option<&str>) -> IntValue<'ctx> { + let pstart = self.ptr_to_start(ctx); + let var_name = name + .map(ToString::to_string) + .or_else(|| self.name.map(|v| format!("{v}.start"))) + .unwrap_or_default(); + + ctx.builder + .build_load(pstart, var_name.as_str()) + .map(BasicValueEnum::into_int_value) + .unwrap() + } + + /// Stores the `end` value into this instance. + pub fn store_end(&self, ctx: &CodeGenContext<'ctx, '_>, end: IntValue<'ctx>) { + debug_assert_eq!(end.get_type().get_bit_width(), 32); + + let pend = self.ptr_to_end(ctx); + ctx.builder.build_store(pend, end).unwrap(); + } + + /// Returns the `end` value of this `range`. + pub fn load_end(&self, ctx: &CodeGenContext<'ctx, '_>, name: Option<&str>) -> IntValue<'ctx> { + let pend = self.ptr_to_end(ctx); + let var_name = name + .map(ToString::to_string) + .or_else(|| self.name.map(|v| format!("{v}.end"))) + .unwrap_or_default(); + + ctx.builder.build_load(pend, var_name.as_str()).map(BasicValueEnum::into_int_value).unwrap() + } + + /// Stores the `step` value into this instance. + pub fn store_step(&self, ctx: &CodeGenContext<'ctx, '_>, step: IntValue<'ctx>) { + debug_assert_eq!(step.get_type().get_bit_width(), 32); + + let pstep = self.ptr_to_step(ctx); + ctx.builder.build_store(pstep, step).unwrap(); + } + + /// Returns the `step` value of this `range`. + pub fn load_step(&self, ctx: &CodeGenContext<'ctx, '_>, name: Option<&str>) -> IntValue<'ctx> { + let pstep = self.ptr_to_step(ctx); + let var_name = name + .map(ToString::to_string) + .or_else(|| self.name.map(|v| format!("{v}.step"))) + .unwrap_or_default(); + + ctx.builder + .build_load(pstep, var_name.as_str()) + .map(BasicValueEnum::into_int_value) + .unwrap() + } +} + +impl<'ctx> ProxyValue<'ctx> for RangeValue<'ctx> { + type Base = PointerValue<'ctx>; + type Type = RangeType<'ctx>; + + fn get_type(&self) -> Self::Type { + RangeType::from_type(self.value.get_type()) + } + + fn as_base_value(&self) -> Self::Base { + self.value + } +} + +impl<'ctx> From> for PointerValue<'ctx> { + fn from(value: RangeValue<'ctx>) -> Self { + value.as_base_value() + } +} diff --git a/nac3core/src/toplevel/builtins.rs b/nac3core/src/toplevel/builtins.rs index 4d88b6ee..d4f9664e 100644 --- a/nac3core/src/toplevel/builtins.rs +++ b/nac3core/src/toplevel/builtins.rs @@ -18,9 +18,9 @@ use super::{ use crate::{ codegen::{ builtin_fns, - classes::{ProxyValue, RangeValue}, numpy::*, stmt::exn_constructor, + values::{ProxyValue, RangeValue}, }, symbol_resolver::SymbolValue, typecheck::typedef::{into_var_map, iter_type_vars, TypeVar, VarMap},