From 0df2f26c9864a42e0e1d0b7daa09f063aef001d9 Mon Sep 17 00:00:00 2001 From: lyken Date: Thu, 15 Aug 2024 11:01:56 +0800 Subject: [PATCH] WIP: core/ndstrides: builtin_fns deleted --- nac3core/src/codegen/builtin_fns.rs | 81 ------------ nac3core/src/codegen/mod.rs | 1 - nac3core/src/codegen/model/array.rs | 122 ++++++++++++++++++ nac3core/src/codegen/model/mod.rs | 2 + nac3core/src/codegen/model/util.rs | 29 ----- nac3core/src/codegen/numpy_new.rs | 108 ---------------- nac3core/src/codegen/object/list.rs | 9 ++ nac3core/src/codegen/object/mod.rs | 44 ++++++- .../src/codegen/object/ndarray/functions.rs | 61 ++++----- .../src/codegen/object/ndarray/mapping.rs | 1 - nac3core/src/codegen/object/ndarray/mod.rs | 5 +- .../src/codegen/object/ndarray/shape_util.rs | 2 +- nac3core/src/codegen/object/range.rs | 41 ++++++ nac3core/src/codegen/object/tuple.rs | 31 ++++- nac3core/src/codegen/structure.rs | 38 +++++- nac3core/src/toplevel/builtins.rs | 17 +-- 16 files changed, 315 insertions(+), 277 deletions(-) delete mode 100644 nac3core/src/codegen/builtin_fns.rs create mode 100644 nac3core/src/codegen/model/array.rs create mode 100644 nac3core/src/codegen/object/range.rs diff --git a/nac3core/src/codegen/builtin_fns.rs b/nac3core/src/codegen/builtin_fns.rs deleted file mode 100644 index 44f104ff..00000000 --- a/nac3core/src/codegen/builtin_fns.rs +++ /dev/null @@ -1,81 +0,0 @@ -use inkwell::values::{BasicValueEnum, IntValue}; -use inkwell::IntPredicate; -use itertools::Itertools; - -use crate::codegen::classes::{ArrayLikeValue, NDArrayValue, RangeValue, TypedArrayLikeAccessor}; -use crate::codegen::expr::destructure_range; -use crate::codegen::irrt::calculate_len_for_slice_range; -use crate::codegen::{CodeGenContext, CodeGenerator}; -use crate::toplevel::helper::PrimDef; -use crate::typecheck::typedef::{Type, TypeEnum}; - -/// Shorthand for [`unreachable!()`] when a type of argument is not supported. -/// -/// The generated message will contain the function name and the name of the unsupported type. -fn unsupported_type(ctx: &CodeGenContext<'_, '_>, fn_name: &str, tys: &[Type]) -> ! { - unreachable!( - "{fn_name}() not supported for '{}'", - tys.iter().map(|ty| format!("'{}'", ctx.unifier.stringify(*ty))).join(", "), - ) -} - -/// Invokes the `len` builtin function. -pub fn call_len<'ctx, G: CodeGenerator + ?Sized>( - generator: &mut G, - ctx: &mut CodeGenContext<'ctx, '_>, - n: (Type, BasicValueEnum<'ctx>), -) -> Result, String> { - let llvm_i32 = ctx.ctx.i32_type(); - let range_ty = ctx.primitives.range; - let (arg_ty, arg) = n; - - Ok(if ctx.unifier.unioned(arg_ty, range_ty) { - let arg = RangeValue::from_ptr_val(arg.into_pointer_value(), Some("range")); - let (start, end, step) = destructure_range(ctx, arg); - calculate_len_for_slice_range(generator, ctx, start, end, step) - } else { - match &*ctx.unifier.get_ty_immutable(arg_ty) { - TypeEnum::TTuple { ty, .. } => llvm_i32.const_int(ty.len() as u64, false), - TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::List.id() => { - let zero = llvm_i32.const_zero(); - let len = ctx - .build_gep_and_load( - arg.into_pointer_value(), - &[zero, llvm_i32.const_int(1, false)], - None, - ) - .into_int_value(); - ctx.builder.build_int_truncate_or_bit_cast(len, llvm_i32, "len").unwrap() - } - TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => { - let llvm_usize = generator.get_size_type(ctx.ctx); - - let arg = NDArrayValue::from_ptr_val(arg.into_pointer_value(), llvm_usize, None); - - let ndims = arg.dim_sizes().size(ctx, generator); - ctx.make_assert( - generator, - ctx.builder - .build_int_compare(IntPredicate::NE, ndims, llvm_usize.const_zero(), "") - .unwrap(), - "0:TypeError", - "len() of unsized object", - [None, None, None], - ctx.current_loc, - ); - - let len = unsafe { - arg.dim_sizes().get_typed_unchecked( - ctx, - generator, - &llvm_usize.const_zero(), - None, - ) - }; - - ctx.builder.build_int_truncate_or_bit_cast(len, llvm_i32, "len").unwrap() - } - _ => unreachable!(), - } - }) -} diff --git a/nac3core/src/codegen/mod.rs b/nac3core/src/codegen/mod.rs index e1065b39..59245d26 100644 --- a/nac3core/src/codegen/mod.rs +++ b/nac3core/src/codegen/mod.rs @@ -35,7 +35,6 @@ use std::sync::{ use std::thread; use structure::{CSlice, Exception, NDArray}; -pub mod builtin_fns; pub mod classes; pub mod concrete_type; pub mod expr; diff --git a/nac3core/src/codegen/model/array.rs b/nac3core/src/codegen/model/array.rs new file mode 100644 index 00000000..58bc7abd --- /dev/null +++ b/nac3core/src/codegen/model/array.rs @@ -0,0 +1,122 @@ +use inkwell::{ + context::Context, + types::{ArrayType, BasicType, BasicTypeEnum}, + values::ArrayValue, +}; + +use crate::codegen::{CodeGenContext, CodeGenerator}; + +use super::*; + +/// A Model for an [`ArrayType`]. +#[derive(Debug, Clone, Copy)] +pub struct ArrayModel { + pub len: u32, + pub element: Element, +} +pub type Array<'ctx, Element> = Instance<'ctx, ArrayModel>; + +impl<'ctx, Element: Model<'ctx>> Model<'ctx> for ArrayModel { + type Value = ArrayValue<'ctx>; + type Type = ArrayType<'ctx>; + + fn get_type(&self, generator: &G, ctx: &'ctx Context) -> Self::Type { + self.element.get_type(generator, ctx).array_type(self.len) + } + + fn check_type, G: CodeGenerator + ?Sized>( + &self, + generator: &mut G, + ctx: &'ctx Context, + ty: T, + ) -> Result<(), ModelError> { + let ty = ty.as_basic_type_enum(); + let BasicTypeEnum::ArrayType(ty) = ty else { + return Err(ModelError(format!("Expecting ArrayType, but got {ty:?}"))); + }; + + if ty.len() != self.len { + return Err(ModelError(format!( + "Expecting ArrayType with size {}, but got an ArrayType with size {}", + ty.len(), + self.len + ))); + } + + self.element + .check_type(generator, ctx, ty.get_element_type()) + .map_err(|err| err.under_context("an ArrayType"))?; + + Ok(()) + } +} + +impl<'ctx, Element: Model<'ctx>> Ptr<'ctx, ArrayModel> { + /// Get the pointer to the `i`-th (0-based) array element. + pub fn at( + &self, + generator: &mut G, + ctx: &CodeGenContext<'ctx, '_>, + i: u32, + name: &str, + ) -> Ptr<'ctx, Element> { + assert!(i < self.model.0.len); + + let zero = ctx.ctx.i32_type().const_zero(); + let i = ctx.ctx.i32_type().const_int(i as u64, false); + let ptr = unsafe { ctx.builder.build_in_bounds_gep(self.value, &[zero, i], name).unwrap() }; + + PtrModel(self.model.0.element).check_value(generator, ctx.ctx, ptr).unwrap() + } +} + +/// Like [`ArrayModel`] but length is strongly-typed. +#[derive(Debug, Clone, Copy, Default)] +pub struct NArrayModel(pub Element); +pub type NArray<'ctx, const LEN: u32, Element> = Instance<'ctx, NArrayModel>; + +impl<'ctx, const LEN: u32, Element: Model<'ctx>> NArrayModel { + /// Forget the `LEN` constant generic and get an [`ArrayModel`] with the same length. + pub fn forget_len(&self) -> ArrayModel { + ArrayModel { element: self.0, len: LEN } + } +} + +impl<'ctx, const LEN: u32, Element: Model<'ctx>> Model<'ctx> for NArrayModel { + type Value = ArrayValue<'ctx>; + type Type = ArrayType<'ctx>; + + fn get_type(&self, generator: &G, ctx: &'ctx Context) -> Self::Type { + // Convenient implementation + self.forget_len().get_type(generator, ctx) + } + + fn check_type, G: CodeGenerator + ?Sized>( + &self, + generator: &mut G, + ctx: &'ctx Context, + ty: T, + ) -> Result<(), ModelError> { + // Convenient implementation + self.forget_len().check_type(generator, ctx, ty) + } +} + +impl<'ctx, const LEN: u32, Element: Model<'ctx>> Ptr<'ctx, NArrayModel> { + /// Get the pointer to the `i`-th (0-based) array element. + pub fn at_const( + &self, + generator: &mut G, + ctx: &CodeGenContext<'ctx, '_>, + i: u32, + name: &str, + ) -> Ptr<'ctx, Element> { + assert!(i < LEN); + + let zero = ctx.ctx.i32_type().const_zero(); + let i = ctx.ctx.i32_type().const_int(i as u64, false); + let ptr = unsafe { ctx.builder.build_in_bounds_gep(self.value, &[zero, i], name).unwrap() }; + + PtrModel(self.model.0 .0).check_value(generator, ctx.ctx, ptr).unwrap() + } +} diff --git a/nac3core/src/codegen/model/mod.rs b/nac3core/src/codegen/model/mod.rs index 0c07224e..4256e84e 100644 --- a/nac3core/src/codegen/model/mod.rs +++ b/nac3core/src/codegen/model/mod.rs @@ -1,4 +1,5 @@ mod any; +mod array; mod core; mod float; pub mod function; @@ -8,6 +9,7 @@ mod structure; pub mod util; pub use any::*; +pub use array::*; pub use core::*; pub use float::*; pub use int::*; diff --git a/nac3core/src/codegen/model/util.rs b/nac3core/src/codegen/model/util.rs index 2b09a259..19f29762 100644 --- a/nac3core/src/codegen/model/util.rs +++ b/nac3core/src/codegen/model/util.rs @@ -60,32 +60,3 @@ where step.value, ) } - -/// Like [`gen_if_callback`] with [`Model`] abstractions and without the `else` block. -pub fn gen_if_model<'ctx, 'a, G, ThenFn>( - generator: &mut G, - ctx: &mut CodeGenContext<'ctx, 'a>, - cond: Int<'ctx, Bool>, - then: ThenFn, -) -> Result<(), String> -where - G: CodeGenerator + ?Sized, - ThenFn: FnOnce(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result<(), String>, -{ - let current_bb = ctx.builder.get_insert_block().unwrap(); - let then_bb = ctx.ctx.insert_basic_block_after(current_bb, "if.then"); - let end_bb = ctx.ctx.insert_basic_block_after(then_bb, "if.end"); - - // Inserting into `current_bb`. - ctx.builder.build_conditional_branch(cond.value, then_bb, end_bb).unwrap(); - - // Inserting into `then_bb` - ctx.builder.position_at_end(then_bb); - then(generator, ctx)?; - ctx.builder.build_unconditional_branch(end_bb).unwrap(); - - // Reposition to `end_bb` for continuation. - ctx.builder.position_at_end(end_bb); - - Ok(()) -} diff --git a/nac3core/src/codegen/numpy_new.rs b/nac3core/src/codegen/numpy_new.rs index 7992331c..c59afbd5 100644 --- a/nac3core/src/codegen/numpy_new.rs +++ b/nac3core/src/codegen/numpy_new.rs @@ -20,113 +20,6 @@ use super::{ CodeGenContext, CodeGenerator, }; - -/// Helper function to create an ndarray with uninitialized values. -/// -/// * `ndarray_ty` - The [`Type`] of the ndarray -/// * `shape` - The user input shape argument -/// * `shape_ty` - The [`Type`] of the shape argument -/// -/// This function does data validation the `shape` input. -fn create_empty_ndarray<'ctx, G>( - generator: &mut G, - ctx: &mut CodeGenContext<'ctx, '_>, - ndarray_ty: Type, - shape: AnyObject<'ctx>, -) -> NDArrayObject<'ctx> -where - G: CodeGenerator + ?Sized, -{ - let (_, shape) = parse_numpy_int_sequence(generator, ctx, shape); - - let ndarray = NDArrayObject::alloca_ndarray_type(generator, ctx, ndarray_ty, "ndarray"); - - // Validate `shape` - let ndims = ndarray.get_ndims(generator, ctx.ctx); - call_nac3_ndarray_util_assert_shape_no_negative(generator, ctx, ndims, shape); - - // Setup `ndarray` with `shape` - ndarray.copy_shape_from_array(generator, ctx, shape); - ndarray.create_data(generator, ctx); // `shape` has to be set - - ndarray -} - -/// Generates LLVM IR for `np.empty`. -pub fn gen_ndarray_empty<'ctx>( - ctx: &mut CodeGenContext<'ctx, '_>, - obj: &Option<(Type, ValueEnum<'ctx>)>, - fun: (&FunSignature, DefinitionId), - args: &[(Option, ValueEnum<'ctx>)], - generator: &mut dyn CodeGenerator, -) -> Result, String> { - assert!(obj.is_none()); - assert_eq!(args.len(), 1); - - // Parse arguments - let shape_ty = fun.0.args[0].ty; - let shape = args[0].1.clone().to_basic_value_enum(ctx, generator, shape_ty)?; - let shape = AnyObject { ty: shape_ty, value: shape }; - - // Implementation - let ndarray_ty = fun.0.ret; - let ndarray = create_empty_ndarray(generator, ctx, ndarray_ty, shape); - - Ok(ndarray.instance.value.as_basic_value_enum()) -} - -/// Generates LLVM IR for `np.zero`. -pub fn gen_ndarray_zeros<'ctx>( - ctx: &mut CodeGenContext<'ctx, '_>, - obj: &Option<(Type, ValueEnum<'ctx>)>, - fun: (&FunSignature, DefinitionId), - args: &[(Option, ValueEnum<'ctx>)], - generator: &mut dyn CodeGenerator, -) -> Result, String> { - assert!(obj.is_none()); - assert_eq!(args.len(), 1); - - // Parse arguments - let shape_ty = fun.0.args[0].ty; - let shape = args[0].1.clone().to_basic_value_enum(ctx, generator, shape_ty)?; - let shape = AnyObject { ty: shape_ty, value: shape }; - - // Implementation - let ndarray_ty = fun.0.ret; - let ndarray = create_empty_ndarray(generator, ctx, ndarray_ty, shape); - - let fill_value = ndarray_zero_value(generator, ctx, ndarray.dtype); - ndarray.fill(generator, ctx, fill_value); - - Ok(ndarray.instance.value.as_basic_value_enum()) -} - -/// Generates LLVM IR for `np.ones`. -pub fn gen_ndarray_ones<'ctx>( - ctx: &mut CodeGenContext<'ctx, '_>, - obj: &Option<(Type, ValueEnum<'ctx>)>, - fun: (&FunSignature, DefinitionId), - args: &[(Option, ValueEnum<'ctx>)], - generator: &mut dyn CodeGenerator, -) -> Result, String> { - assert!(obj.is_none()); - assert_eq!(args.len(), 1); - - // Parse arguments - let shape_ty = fun.0.args[0].ty; - let shape = args[0].1.clone().to_basic_value_enum(ctx, generator, shape_ty)?; - let shape = AnyObject { ty: shape_ty, value: shape }; - - // Implementation - let ndarray_ty = fun.0.ret; - let ndarray = create_empty_ndarray(generator, ctx, ndarray_ty, shape); - - let fill_value = ndarray_one_value(generator, ctx, ndarray.dtype); - ndarray.fill(generator, ctx, fill_value); - - Ok(ndarray.instance.value.as_basic_value_enum()) -} - /// Generates LLVM IR for `np.broadcast_to`. pub fn gen_ndarray_broadcast_to<'ctx>( ctx: &mut CodeGenContext<'ctx, '_>, @@ -315,4 +208,3 @@ pub fn gen_ndarray_strides<'ctx>( let strides = TupleObject::create(generator, ctx, objects, "strides"); Ok(strides.value.as_basic_value_enum()) } - diff --git a/nac3core/src/codegen/object/list.rs b/nac3core/src/codegen/object/list.rs index 05a49f46..0821e70c 100644 --- a/nac3core/src/codegen/object/list.rs +++ b/nac3core/src/codegen/object/list.rs @@ -75,4 +75,13 @@ impl<'ctx> ListObject<'ctx> { opaque_list_ptr } + + /// Get the `len()` of this list. + pub fn len( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + ) -> Int<'ctx, SizeT> { + self.instance.get(generator, ctx, |f| f.len, "list_len") + } } diff --git a/nac3core/src/codegen/object/mod.rs b/nac3core/src/codegen/object/mod.rs index 15710a4f..729d6984 100644 --- a/nac3core/src/codegen/object/mod.rs +++ b/nac3core/src/codegen/object/mod.rs @@ -1,9 +1,16 @@ use inkwell::values::BasicValueEnum; +use list::ListObject; +use ndarray::NDArrayObject; +use range::RangeObject; +use tuple::TupleObject; -use crate::typecheck::typedef::Type; +use crate::typecheck::typedef::{Type, TypeEnum}; + +use super::{model::*, CodeGenContext, CodeGenerator}; pub mod list; pub mod ndarray; +pub mod range; pub mod tuple; #[derive(Debug, Clone, Copy)] @@ -11,3 +18,38 @@ pub struct AnyObject<'ctx> { pub ty: Type, pub value: BasicValueEnum<'ctx>, } + +impl<'ctx> AnyObject<'ctx> { + // Get the `len()` of this object. + pub fn len( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + ) -> Int<'ctx, Int32> { + match &*ctx.unifier.get_ty_immutable(self.ty) { + TypeEnum::TTuple { .. } => { + let tuple = TupleObject::from_object(ctx, *self); + tuple.len(generator, ctx).truncate(generator, ctx, Int32, "tuple_len_32") + } + TypeEnum::TObj { obj_id, .. } + if *obj_id == ctx.primitives.range.obj_id(&ctx.unifier).unwrap() => + { + let range = RangeObject::from_object(generator, ctx, *self); + range.len(generator, ctx) + } + TypeEnum::TObj { obj_id, .. } + if *obj_id == ctx.primitives.list.obj_id(&ctx.unifier).unwrap() => + { + let list = ListObject::from_object(generator, ctx, *self); + list.len(generator, ctx).truncate(generator, ctx, Int32, "list_len_i32") + } + TypeEnum::TObj { obj_id, .. } + if *obj_id == ctx.primitives.ndarray.obj_id(&ctx.unifier).unwrap() => + { + let ndarray = NDArrayObject::from_object(generator, ctx, *self); + ndarray.len(generator, ctx).truncate(generator, ctx, Int32, "ndarray_len_i32") + } + _ => unreachable!(), + } + } +} diff --git a/nac3core/src/codegen/object/ndarray/functions.rs b/nac3core/src/codegen/object/ndarray/functions.rs index d262ac23..4ba9a0e0 100644 --- a/nac3core/src/codegen/object/ndarray/functions.rs +++ b/nac3core/src/codegen/object/ndarray/functions.rs @@ -5,14 +5,7 @@ use inkwell::{ use itertools::Itertools; use crate::{ - codegen::{ - llvm_intrinsics, - model::{ - util::{gen_for_model_auto, gen_if_model}, - *, - }, - CodeGenContext, CodeGenerator, - }, + codegen::{llvm_intrinsics, model::*, stmt::gen_if_callback, CodeGenContext, CodeGenerator}, typecheck::typedef::Type, }; @@ -483,38 +476,40 @@ impl<'ctx> NDArrayObject<'ctx> { let first_scalar = self.get_nth_scalar(generator, ctx, zero); ctx.builder.build_store(pextremum, first_scalar.value).unwrap(); - // Find extremum - let start = sizet_model.const_1(generator, ctx.ctx); // Start on 1 - let stop = self.size(generator, ctx); - let step = sizet_model.const_1(generator, ctx.ctx); - gen_for_model_auto(generator, ctx, start, stop, step, |generator, ctx, _hooks, i| { - // Worth reading on "Notes" in - // on how `NaN` values have to be handled. - - let scalar = self.get_nth_scalar(generator, ctx, i); - + self.foreach(generator, ctx, |generator, ctx, _hooks, nditer| { let old_extremum = ctx.builder.build_load(pextremum, "current_extremum").unwrap(); let old_extremum = ScalarObject { dtype: self.dtype, value: old_extremum }; + let scalar = nditer.get_scalar(generator, ctx); let new_extremum = ScalarObject::min_or_max(ctx, kind, old_extremum, scalar); - // Check if new_extremum is more extreme than old_extremum. - let update_index = ScalarObject::compare( + gen_if_callback( generator, ctx, - new_extremum, - old_extremum, - IntPredicate::NE, - FloatPredicate::ONE, - "", - ); - - gen_if_model(generator, ctx, update_index, |_generator, ctx| { - pextremum_index.store(ctx, i); - Ok(()) - }) - .unwrap(); - Ok(()) + |generator, ctx| { + // Is new_extremum is more extreme than old_extremum? + let cmp = ScalarObject::compare( + generator, + ctx, + new_extremum, + old_extremum, + IntPredicate::NE, + FloatPredicate::ONE, + "", + ); + Ok(cmp.value) + }, + |generator, ctx| { + // Yes, update the extremum index + let index = nditer.get_index(generator, ctx); + pextremum_index.store(ctx, index); + Ok(()) + }, + |_generator, _ctx| { + // No, do nothing + Ok(()) + }, + ) }) .unwrap(); diff --git a/nac3core/src/codegen/object/ndarray/mapping.rs b/nac3core/src/codegen/object/ndarray/mapping.rs index b1deebeb..6d30019d 100644 --- a/nac3core/src/codegen/object/ndarray/mapping.rs +++ b/nac3core/src/codegen/object/ndarray/mapping.rs @@ -3,7 +3,6 @@ use itertools::Itertools; use crate::{ codegen::{ - model::*, object::ndarray::{NDArrayObject, ScalarObject}, stmt::gen_for_callback, CodeGenContext, CodeGenerator, diff --git a/nac3core/src/codegen/object/ndarray/mod.rs b/nac3core/src/codegen/object/ndarray/mod.rs index e4c6537f..d7659bb7 100644 --- a/nac3core/src/codegen/object/ndarray/mod.rs +++ b/nac3core/src/codegen/object/ndarray/mod.rs @@ -17,8 +17,7 @@ use crate::{ call_nac3_ndarray_is_c_contiguous, call_nac3_ndarray_len, call_nac3_ndarray_nbytes, call_nac3_ndarray_resolve_and_check_new_shape, call_nac3_ndarray_set_strides_by_shape, call_nac3_ndarray_size, call_nac3_ndarray_transpose, - call_nac3_ndarray_util_assert_output_shape_same, call_nac3_nditer_has_next, - call_nac3_nditer_initialize, call_nac3_nditer_next, + call_nac3_ndarray_util_assert_output_shape_same, }, model::*, stmt::{gen_for_callback, BreakContinueHooks}, @@ -40,7 +39,7 @@ use inkwell::{ }; use nditer::NDIterHandle; use scalar::{ScalarObject, ScalarOrNDArray}; -use util::{call_memcpy_model, gen_for_model_auto}; +use util::call_memcpy_model; use super::{tuple::TupleObject, AnyObject}; diff --git a/nac3core/src/codegen/object/ndarray/shape_util.rs b/nac3core/src/codegen/object/ndarray/shape_util.rs index dec32382..ef051a5e 100644 --- a/nac3core/src/codegen/object/ndarray/shape_util.rs +++ b/nac3core/src/codegen/object/ndarray/shape_util.rs @@ -71,7 +71,7 @@ pub fn parse_numpy_int_sequence<'ctx, G: CodeGenerator + ?Sized>( let input_sequence = TupleObject::from_object(ctx, input_sequence); - let len_int = input_sequence.len(); + let len_int = input_sequence.len_static(); let len = sizet_model.constant(generator, ctx.ctx, len_int as u64); let result = sizet_model.array_alloca(generator, ctx, len.value, "int_sequence"); diff --git a/nac3core/src/codegen/object/range.rs b/nac3core/src/codegen/object/range.rs new file mode 100644 index 00000000..2f1f1198 --- /dev/null +++ b/nac3core/src/codegen/object/range.rs @@ -0,0 +1,41 @@ +use crate::codegen::{ + irrt::calculate_len_for_slice_range, model::*, structure::RangeModel, CodeGenContext, + CodeGenerator, +}; + +use super::AnyObject; + +/// A `range` in NAC3 +pub struct RangeObject<'ctx> { + pub instance: Ptr<'ctx, RangeModel>, +} + +impl<'ctx> RangeObject<'ctx> { + pub fn from_object( + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + object: AnyObject<'ctx>, + ) -> Self { + assert!(ctx.unifier.unioned(ctx.primitives.range, object.ty)); // Sanity check on type. + + let model = PtrModel(RangeModel::default()); + let instance = model.check_value(generator, ctx.ctx, object.value).unwrap(); + RangeObject { instance } + } + + /// Get the `len()` of this range. + pub fn len( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + ) -> Int<'ctx, Int32> { + let start = self.instance.gep_start(generator, ctx, "").load(generator, ctx, "start"); + let stop = self.instance.gep_stop(generator, ctx, "").load(generator, ctx, "stop"); + let step = self.instance.gep_step(generator, ctx, "").load(generator, ctx, "step"); + + // TODO: Refactor this + let len = + calculate_len_for_slice_range(generator, ctx, start.value, stop.value, step.value); + IntModel(Int32).check_value(generator, ctx.ctx, len).unwrap() + } +} diff --git a/nac3core/src/codegen/object/tuple.rs b/nac3core/src/codegen/object/tuple.rs index ce0826af..2440d79c 100644 --- a/nac3core/src/codegen/object/tuple.rs +++ b/nac3core/src/codegen/object/tuple.rs @@ -4,7 +4,7 @@ use inkwell::values::StructValue; use itertools::Itertools; use crate::{ - codegen::{CodeGenContext, CodeGenerator}, + codegen::{model::*, CodeGenContext, CodeGenerator}, typecheck::typedef::{Type, TypeEnum}, }; @@ -22,10 +22,13 @@ pub struct TupleObject<'ctx> { } impl<'ctx> TupleObject<'ctx> { - // NOTE: There is no Model abstraction for Tuples. Everything has to be done raw with Inkwell. + // NOTE: There is no Model abstraction for Tuples with arbitrary lengths. + // Everything has to be done raw with Inkwell. pub fn from_object(ctx: &mut CodeGenContext<'ctx, '_>, object: AnyObject<'ctx>) -> Self { // TODO: Keep `is_vararg_ctx` from TTuple? + + // Sanity check on object type. let TypeEnum::TTuple { ty: tys, .. } = &*ctx.unifier.get_ty(object.ty) else { panic!( "Expected type to be a TypeEnum::TTuple, got {}", @@ -71,23 +74,37 @@ impl<'ctx> TupleObject<'ctx> { TupleObject { tys, value } } - /// Get the `len()` of this tuple. + /// Get the `len()` of this tuple statically. /// - /// We statically know the lengths of tuples in NAC3. + /// We statically know the lengths of tuples in NAC3 when compiling. #[must_use] - pub fn len(&self) -> usize { + pub fn len_static(&self) -> usize { self.tys.len() } + /// Get the `len()` of this tuple. + #[must_use] + pub fn len( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + ) -> Int<'ctx, SizeT> { + IntModel(SizeT).constant(generator, ctx.ctx, self.len_static() as u64) + } + /// Check if this tuple is an empty/unit tuple. #[must_use] pub fn is_empty(&self) -> bool { - self.len() == 0 + self.len_static() == 0 } /// Get the `i`-th (0-based) object in this tuple. pub fn get(&self, ctx: &mut CodeGenContext<'ctx, '_>, i: usize, name: &str) -> AnyObject<'ctx> { - assert!(i < self.len(), "Tuple object with length {} have index {i}", self.len()); + assert!( + i < self.len_static(), + "Tuple object with length {} have index {i}", + self.len_static() + ); let value = ctx.builder.build_extract_value(self.value, i as u32, name).unwrap(); let ty = self.tys[i]; diff --git a/nac3core/src/codegen/structure.rs b/nac3core/src/codegen/structure.rs index ce43f2ae..03bbbc23 100644 --- a/nac3core/src/codegen/structure.rs +++ b/nac3core/src/codegen/structure.rs @@ -2,7 +2,7 @@ use inkwell::context::Context; use crate::codegen::model::*; -use super::CodeGenerator; +use super::{CodeGenContext, CodeGenerator}; /// Fields of [`CSlice`] pub struct CSliceFields<'ctx, F: FieldTraversal<'ctx>> { @@ -186,7 +186,6 @@ impl<'ctx, Item: Model<'ctx>> StructKind<'ctx> for SimpleNDArray { } } -/// An IRRT helper structure used when iterating through an ndarray. /// Fields of [`NDIter`] pub struct NDIterFields<'ctx, F: FieldTraversal<'ctx>> { pub ndims: F::Out>, @@ -200,6 +199,7 @@ pub struct NDIterFields<'ctx, F: FieldTraversal<'ctx>> { pub size: F::Out>, } +/// An IRRT helper structure used when iterating through an ndarray. #[derive(Debug, Clone, Copy, Default)] pub struct NDIter; @@ -220,3 +220,37 @@ impl<'ctx> StructKind<'ctx> for NDIter { } } } + +/// A NAC3 `range`. It is an array of 3 int32s. +// TODO: Use `pub type RangeModel = NArrayModel<3, IntModel>` in the future when +// `range` type is type dependent. +pub type RangeModel = NArrayModel<3, IntModel>; + +impl<'ctx> Ptr<'ctx, RangeModel> { + pub fn gep_start( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + name: &str, + ) -> Ptr<'ctx, IntModel> { + self.at_const(generator, ctx, 0, name) + } + + pub fn gep_stop( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + name: &str, + ) -> Ptr<'ctx, IntModel> { + self.at_const(generator, ctx, 1, name) + } + + pub fn gep_step( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + name: &str, + ) -> Ptr<'ctx, IntModel> { + self.at_const(generator, ctx, 2, name) + } +} diff --git a/nac3core/src/toplevel/builtins.rs b/nac3core/src/toplevel/builtins.rs index 49bda0db..e41db92a 100644 --- a/nac3core/src/toplevel/builtins.rs +++ b/nac3core/src/toplevel/builtins.rs @@ -1,4 +1,4 @@ -use std::iter::once; +use std::{any::Any, iter::once}; use helper::{ create_ndims, debug_assert_prim_is_allowed, extract_ndims, make_exception_fields, @@ -17,10 +17,9 @@ use strum::IntoEnumIterator; use crate::{ codegen::{ - builtin_fns, classes::{ProxyValue, RangeValue}, extern_fns::{self, call_np_linalg_det, call_np_linalg_matrix_power}, - irrt::{self, call_nac3_ndarray_util_assert_shape_no_negative}, + irrt::{self}, llvm_intrinsics, model::*, numpy::*, @@ -1294,7 +1293,7 @@ impl<'a> BuiltinBuilder<'a> { prim.name(), self.ndarray_float, &[(self.ndarray_factory_fn_shape_arg_tvar.ty, "shape")], - Box::new(move |ctx, obj, fun, args, generator| { + Box::new(move |ctx, _obj, fun, args, generator| { // Parse argument `shape`. let shape_ty = fun.0.args[0].ty; let shape_arg = args[0].1.clone().to_basic_value_enum(ctx, generator, shape_ty)?; @@ -1559,7 +1558,7 @@ impl<'a> BuiltinBuilder<'a> { (array_tvar.ty, "array"), (self.ndarray_factory_fn_shape_arg_tvar.ty, "shape"), ], - Box::new(move |ctx, obj, fun, args, generator| { + Box::new(move |ctx, _obj, fun, args, generator| { // Parse argument #1 ndarray let input_ty = fun.0.args[0].ty; let input = @@ -1631,10 +1630,7 @@ impl<'a> BuiltinBuilder<'a> { instance_to_stmt: HashMap::default(), resolver: None, codegen_callback: Some(Arc::new(GenCall::new(Box::new( - |ctx, obj, fun, args, generator| { - assert!(obj.is_none()); - assert_eq!(args.len(), 1); - + |ctx, _obj, fun, args, generator| { // Parse argument #1 ndarray let ndarray_ty = fun.0.args[0].ty; let ndarray = args[0] @@ -1843,8 +1839,9 @@ impl<'a> BuiltinBuilder<'a> { move |ctx, _, fun, args, generator| { let arg_ty = fun.0.args[0].ty; let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?; + let arg = AnyObject { value: arg, ty: arg_ty }; - builtin_fns::call_len(generator, ctx, (arg_ty, arg)).map(|ret| Some(ret.into())) + Ok(Some(arg.len(generator, ctx).value)) }, )))), loc: None,