From acd976289f82cb8e4c113631ade97679fabc28ac Mon Sep 17 00:00:00 2001 From: David Mak Date: Thu, 21 Nov 2024 14:25:05 +0800 Subject: [PATCH] WIP --- nac3artiq/src/codegen.rs | 4 +- nac3core/src/codegen/expr.rs | 128 +++++-- nac3core/src/codegen/irrt/mod.rs | 21 ++ nac3core/src/codegen/irrt/ndarray/basic.rs | 384 ++++++++++++++------- nac3core/src/codegen/llvm_intrinsics.rs | 46 +++ nac3core/src/codegen/numpy.rs | 17 +- nac3core/src/codegen/types/ndarray.rs | 38 +- nac3core/src/codegen/types/structure.rs | 2 +- nac3core/src/codegen/values/ndarray.rs | 151 +++++++- 9 files changed, 605 insertions(+), 186 deletions(-) diff --git a/nac3artiq/src/codegen.rs b/nac3artiq/src/codegen.rs index a89d018c..529906ce 100644 --- a/nac3artiq/src/codegen.rs +++ b/nac3artiq/src/codegen.rs @@ -721,7 +721,9 @@ fn format_rpc_ret<'ctx>( ); } - ndarray.create_data(generator, ctx, llvm_elem_ty, num_elements); + unsafe { + ndarray.create_data(generator, ctx, num_elements); + } let ndarray_data = ndarray.data().base_ptr(ctx, generator); let ndarray_data_i8 = diff --git a/nac3core/src/codegen/expr.rs b/nac3core/src/codegen/expr.rs index ec2b3458..5302730a 100644 --- a/nac3core/src/codegen/expr.rs +++ b/nac3core/src/codegen/expr.rs @@ -32,7 +32,7 @@ use super::{ gen_for_callback_incrementing, gen_if_callback, gen_if_else_expr_callback, gen_raise, gen_var, }, - types::{ListType, ProxyType}, + types::{ListType, NDArrayType, ProxyType}, values::{ ArrayLikeIndexer, ArrayLikeValue, ListValue, NDArrayValue, ProxyValue, RangeValue, TypedArrayLikeAccessor, UntypedArrayLikeAccessor, @@ -43,7 +43,7 @@ use crate::{ symbol_resolver::{SymbolValue, ValueEnum}, toplevel::{ helper::PrimDef, - numpy::{make_ndarray_ty, unpack_ndarray_var_tys}, + numpy::unpack_ndarray_var_tys, DefinitionId, TopLevelDef, }, typecheck::{ @@ -2595,14 +2595,6 @@ fn gen_ndarray_subscript_expr<'ctx, G: CodeGenerator>( _ => 1, }; - let ndarray_ndims_ty = ctx.unifier.get_fresh_literal( - ndims.iter().map(|v| SymbolValue::U64(v - subscripted_dims)).collect(), - None, - ); - let ndarray_ty = - make_ndarray_ty(&mut ctx.unifier, &ctx.primitives, Some(ty), Some(ndarray_ndims_ty)); - let llvm_pndarray_t = ctx.get_llvm_type(generator, ndarray_ty).into_pointer_type(); - let llvm_ndarray_t = llvm_pndarray_t.get_element_type().into_struct_type(); let llvm_ndarray_data_t = ctx.get_llvm_type(generator, ty).as_basic_type_enum(); let sizeof_elem = llvm_ndarray_data_t.size_of().unwrap(); @@ -2797,26 +2789,15 @@ fn gen_ndarray_subscript_expr<'ctx, G: CodeGenerator>( let Some(index_addr) = make_indices_arr(generator, ctx)? else { return Ok(None) }; + let num_dims = v.load_ndims(ctx); + let num_dims = ctx.builder + .build_int_sub(num_dims, llvm_usize.const_int(1, false), "") + .unwrap(); + // Create a new array, remove the top dimension from the dimension-size-list, and copy the // elements over - let subscripted_ndarray = - generator.gen_var_alloc(ctx, llvm_ndarray_t.into(), None)?; - let ndarray = NDArrayValue::from_pointer_value( - subscripted_ndarray, - llvm_ndarray_data_t, - None, - llvm_usize, - None, - ); - - let num_dims = v.load_ndims(ctx); - ndarray.store_ndims( - ctx, - generator, - ctx.builder - .build_int_sub(num_dims, llvm_usize.const_int(1, false), "") - .unwrap(), - ); + let ndarray = NDArrayType::new(generator, ctx.ctx, llvm_ndarray_data_t) + .construct_uninitialized(generator, ctx, num_dims, None); let ndarray_num_dims = ndarray.load_ndims(ctx); ndarray.create_shape(ctx, llvm_usize, ndarray_num_dims); @@ -2858,7 +2839,9 @@ fn gen_ndarray_subscript_expr<'ctx, G: CodeGenerator>( .builder .build_int_z_extend_or_bit_cast(ndarray_num_elems, sizeof_elem.get_type(), "") .unwrap(); - ndarray.create_data(generator, ctx, llvm_ndarray_data_t, ndarray_num_elems); + unsafe { + ndarray.create_data(generator, ctx, ndarray_num_elems); + } let v_data_src_ptr = v.data().ptr_offset(ctx, generator, &index_addr, None); call_memcpy_generic( @@ -3604,3 +3587,90 @@ pub fn gen_expr<'ctx, G: CodeGenerator>( _ => unimplemented!(), })) } + +/// Creates a function in the current module and inserts a `call` instruction into the LLVM IR. +pub fn create_fn_and_call<'ctx>( + ctx: &CodeGenContext<'ctx, '_>, + fn_name: &str, + ret_type: Option>, + (params, is_var_args): (&[BasicTypeEnum<'ctx>], bool), + args: &[BasicValueEnum<'ctx>], + call_value_name: Option<&str>, + configure: Option<&dyn Fn(&FunctionValue<'ctx>)>, +) -> Option> { + let intrinsic_fn = ctx.module.get_function(fn_name).unwrap_or_else(|| { + let params = params.iter().copied().map(BasicTypeEnum::into).collect_vec(); + let fn_type = if let Some(ret_type) = ret_type { + ret_type.fn_type(params.as_slice(), is_var_args) + } else { + ctx.ctx.void_type().fn_type(params.as_slice(), is_var_args) + }; + + ctx.module.add_function(fn_name, fn_type, None) + }); + + if let Some(configure) = configure { + configure(&intrinsic_fn); + } + + let args = args.iter().copied().map(BasicValueEnum::into).collect_vec(); + ctx.builder + .build_call(intrinsic_fn, args.as_slice(), call_value_name.unwrap_or_default()) + .map(CallSiteValue::try_as_basic_value) + .map(Either::left) + .unwrap() +} + +/// Creates a function in the current module and inserts a `call` instruction into the LLVM IR. +/// +/// This is a wrapper around [`create_fn_and_call`] for non-vararg function. This function allows +/// parameters and arguments to be specified as tuples to better indicate the expected type and +/// actual value of each parameter-argument pair of the call. +pub fn create_and_call_function<'ctx>( + ctx: &CodeGenContext<'ctx, '_>, + fn_name: &str, + ret_type: Option>, + params: &[(BasicTypeEnum<'ctx>, BasicValueEnum<'ctx>)], + value_name: Option<&str>, + configure: Option<&dyn Fn(&FunctionValue<'ctx>)>, +) -> Option> { + let param_tys = params.iter().map(|(ty, _)| ty).copied().map(BasicTypeEnum::into).collect_vec(); + let arg_values = + params.iter().map(|(_, value)| value).copied().map(BasicValueEnum::into).collect_vec(); + + create_fn_and_call( + ctx, + fn_name, + ret_type, + (param_tys.as_slice(), false), + arg_values.as_slice(), + value_name, + configure, + ) +} + +/// Creates a function in the current module and inserts a `call` instruction into the LLVM IR. +/// +/// This is a wrapper around [`create_fn_and_call`] for non-vararg function. This function allows +/// only arguments to be specified and performs inference for the parameter types using +/// [`BasicValueEnum::get_type`]. +pub fn infer_and_call_function<'ctx>( + ctx: &CodeGenContext<'ctx, '_>, + fn_name: &str, + ret_type: Option>, + args: &[BasicValueEnum<'ctx>], + value_name: Option<&str>, + configure: Option<&dyn Fn(&FunctionValue<'ctx>)>, +) -> Option> { + let param_tys = args.iter().map(BasicValueEnum::get_type).collect_vec(); + + create_fn_and_call( + ctx, + fn_name, + ret_type, + (param_tys.as_slice(), false), + args, + value_name, + configure, + ) +} diff --git a/nac3core/src/codegen/irrt/mod.rs b/nac3core/src/codegen/irrt/mod.rs index 970dbf27..9e3bf4a0 100644 --- a/nac3core/src/codegen/irrt/mod.rs +++ b/nac3core/src/codegen/irrt/mod.rs @@ -59,6 +59,27 @@ pub fn load_irrt<'ctx>(ctx: &'ctx Context, symbol_resolver: &dyn SymbolResolver) irrt_mod } +/// Returns the name of a function which contains variants for 32-bit and 64-bit `size_t`. +/// +/// - When [`TypeContext::size_type`] is 32-bits, the function name is `fn_name}`. +/// - When [`TypeContext::size_type`] is 64-bits, the function name is `{fn_name}64`. +#[must_use] +pub fn get_usize_dependent_function_name( + generator: &mut G, + ctx: &CodeGenContext<'_, '_>, + name: &str, +) -> String { + let mut name = name.to_owned(); + match generator.get_size_type(ctx.ctx).get_bit_width() { + 32 => {} + 64 => name.push_str("64"), + bit_width => { + panic!("Unsupported int type bit width {bit_width}, must be either 32-bits or 64-bits") + } + } + name +} + /// NOTE: the output value of the end index of this function should be compared ***inclusively***, /// because python allows `a[2::-1]`, whose semantic is `[a[2], a[1], a[0]]`, which is equivalent to /// NO numeric slice in python. diff --git a/nac3core/src/codegen/irrt/ndarray/basic.rs b/nac3core/src/codegen/irrt/ndarray/basic.rs index e52031d0..23e1afd9 100644 --- a/nac3core/src/codegen/irrt/ndarray/basic.rs +++ b/nac3core/src/codegen/irrt/ndarray/basic.rs @@ -1,134 +1,258 @@ -use crate::codegen::{CodeGenContext, CodeGenerator}; +use inkwell::{ + values::{BasicValueEnum, IntValue, PointerValue}, + AddressSpace, +}; -/// Returns the name of a function which contains variants for 32-bit and 64-bit `size_t`. -/// -/// - When [`TypeContext::size_type`] is 32-bits, the function name is `fn_name}`. -/// - When [`TypeContext::size_type`] is 64-bits, the function name is `{fn_name}64`. -#[must_use] -pub fn get_usize_dependent_function_name( +use crate::codegen::{ + expr::create_and_call_function, + irrt::get_usize_dependent_function_name, + types::NDArrayType, + values::{NDArrayValue, ProxyValue}, + CodeGenContext, CodeGenerator, +}; + +pub fn call_nac3_ndarray_util_assert_shape_no_negative<'ctx, G: CodeGenerator + ?Sized>( generator: &mut G, - ctx: &CodeGenContext<'_, '_>, - name: &str, -) -> String { - let mut name = name.to_owned(); - match generator.get_size_type(ctx.ctx).get_bit_width() { - 32 => {} - 64 => name.push_str("64"), - bit_width => { - panic!("Unsupported int type bit width {bit_width}, must be either 32-bits or 64-bits") - } - } - name + ctx: &mut CodeGenContext<'ctx, '_>, + ndims: IntValue<'ctx>, + shape: PointerValue<'ctx>, +) { + let llvm_usize = generator.get_size_type(ctx.ctx); + let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default()); + + let name = get_usize_dependent_function_name( + generator, + ctx, + "__nac3_ndarray_util_assert_shape_no_negative", + ); + + create_and_call_function( + ctx, + &name, + Some(llvm_usize.into()), + &[(llvm_usize.into(), ndims.into()), (llvm_pusize.into(), shape.into())], + None, + None, + ); } -// pub fn call_nac3_ndarray_util_assert_shape_no_negative<'ctx, G: CodeGenerator + ?Sized>( -// generator: &mut G, -// ctx: &mut CodeGenContext<'ctx, '_>, -// ndims: Instance<'ctx, Int>, -// shape: Instance<'ctx, Ptr>>, -// ) { -// let name = get_usize_dependent_function_name( -// generator, -// ctx, -// "__nac3_ndarray_util_assert_shape_no_negative", -// ); -// FnCall::builder(generator, ctx, &name).arg(ndims).arg(shape).returning_void(); -// } -// -// pub fn call_nac3_ndarray_util_assert_output_shape_same<'ctx, G: CodeGenerator + ?Sized>( -// generator: &mut G, -// ctx: &mut CodeGenContext<'ctx, '_>, -// ndarray_ndims: Instance<'ctx, Int>, -// ndarray_shape: Instance<'ctx, Ptr>>, -// output_ndims: Instance<'ctx, Int>, -// output_shape: Instance<'ctx, Ptr>>, -// ) { -// let name = get_usize_dependent_function_name( -// generator, -// ctx, -// "__nac3_ndarray_util_assert_output_shape_same", -// ); -// FnCall::builder(generator, ctx, &name) -// .arg(ndarray_ndims) -// .arg(ndarray_shape) -// .arg(output_ndims) -// .arg(output_shape) -// .returning_void(); -// } -// -// pub fn call_nac3_ndarray_size<'ctx, G: CodeGenerator + ?Sized>( -// generator: &mut G, -// ctx: &mut CodeGenContext<'ctx, '_>, -// ndarray: Instance<'ctx, Ptr>>, -// ) -> Instance<'ctx, Int> { -// let name = get_usize_dependent_function_name(generator, ctx, "__nac3_ndarray_size"); -// FnCall::builder(generator, ctx, &name).arg(ndarray).returning_auto("size") -// } -// -// pub fn call_nac3_ndarray_nbytes<'ctx, G: CodeGenerator + ?Sized>( -// generator: &mut G, -// ctx: &mut CodeGenContext<'ctx, '_>, -// ndarray: Instance<'ctx, Ptr>>, -// ) -> Instance<'ctx, Int> { -// let name = get_usize_dependent_function_name(generator, ctx, "__nac3_ndarray_nbytes"); -// FnCall::builder(generator, ctx, &name).arg(ndarray).returning_auto("nbytes") -// } -// -// pub fn call_nac3_ndarray_len<'ctx, G: CodeGenerator + ?Sized>( -// generator: &mut G, -// ctx: &mut CodeGenContext<'ctx, '_>, -// ndarray: Instance<'ctx, Ptr>>, -// ) -> Instance<'ctx, Int> { -// let name = get_usize_dependent_function_name(generator, ctx, "__nac3_ndarray_len"); -// FnCall::builder(generator, ctx, &name).arg(ndarray).returning_auto("len") -// } -// -// pub fn call_nac3_ndarray_is_c_contiguous<'ctx, G: CodeGenerator + ?Sized>( -// generator: &mut G, -// ctx: &mut CodeGenContext<'ctx, '_>, -// ndarray: Instance<'ctx, Ptr>>, -// ) -> Instance<'ctx, Int> { -// let name = get_usize_dependent_function_name(generator, ctx, "__nac3_ndarray_is_c_contiguous"); -// FnCall::builder(generator, ctx, &name).arg(ndarray).returning_auto("is_c_contiguous") -// } -// -// pub fn call_nac3_ndarray_get_nth_pelement<'ctx, G: CodeGenerator + ?Sized>( -// generator: &mut G, -// ctx: &mut CodeGenContext<'ctx, '_>, -// ndarray: Instance<'ctx, Ptr>>, -// index: Instance<'ctx, Int>, -// ) -> Instance<'ctx, Ptr>> { -// let name = get_usize_dependent_function_name(generator, ctx, "__nac3_ndarray_get_nth_pelement"); -// FnCall::builder(generator, ctx, &name).arg(ndarray).arg(index).returning_auto("pelement") -// } -// -// pub fn call_nac3_ndarray_get_pelement_by_indices<'ctx, G: CodeGenerator + ?Sized>( -// generator: &mut G, -// ctx: &mut CodeGenContext<'ctx, '_>, -// ndarray: Instance<'ctx, Ptr>>, -// indices: Instance<'ctx, Ptr>>, -// ) -> Instance<'ctx, Ptr>> { -// let name = -// get_usize_dependent_function_name(generator, ctx, "__nac3_ndarray_get_pelement_by_indices"); -// FnCall::builder(generator, ctx, &name).arg(ndarray).arg(indices).returning_auto("pelement") -// } -// -// pub fn call_nac3_ndarray_set_strides_by_shape<'ctx, G: CodeGenerator + ?Sized>( -// generator: &mut G, -// ctx: &mut CodeGenContext<'ctx, '_>, -// ndarray: Instance<'ctx, Ptr>>, -// ) { -// let name = -// get_usize_dependent_function_name(generator, ctx, "__nac3_ndarray_set_strides_by_shape"); -// FnCall::builder(generator, ctx, &name).arg(ndarray).returning_void(); -// } -// -// pub fn call_nac3_ndarray_copy_data<'ctx, G: CodeGenerator + ?Sized>( -// generator: &mut G, -// ctx: &mut CodeGenContext<'ctx, '_>, -// src_ndarray: Instance<'ctx, Ptr>>, -// dst_ndarray: Instance<'ctx, Ptr>>, -// ) { -// let name = get_usize_dependent_function_name(generator, ctx, "__nac3_ndarray_copy_data"); -// FnCall::builder(generator, ctx, &name).arg(src_ndarray).arg(dst_ndarray).returning_void(); -// } +pub fn call_nac3_ndarray_util_assert_output_shape_same<'ctx, G: CodeGenerator + ?Sized>( + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + ndarray_ndims: IntValue<'ctx>, + ndarray_shape: PointerValue<'ctx>, + output_ndims: IntValue<'ctx>, + output_shape: IntValue<'ctx>, +) { + let llvm_usize = generator.get_size_type(ctx.ctx); + let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default()); + + let name = get_usize_dependent_function_name( + generator, + ctx, + "__nac3_ndarray_util_assert_output_shape_same", + ); + + create_and_call_function( + ctx, + &name, + Some(llvm_usize.into()), + &[ + (llvm_usize.into(), ndarray_ndims.into()), + (llvm_pusize.into(), ndarray_shape.into()), + (llvm_usize.into(), output_ndims.into()), + (llvm_pusize.into(), output_shape.into()), + ], + None, + None, + ); +} + +pub fn call_nac3_ndarray_size<'ctx, G: CodeGenerator + ?Sized>( + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + ndarray: NDArrayValue<'ctx>, +) -> IntValue<'ctx> { + let llvm_usize = generator.get_size_type(ctx.ctx); + let llvm_ndarray = NDArrayType::llvm_type(ctx.ctx, llvm_usize); + + let name = get_usize_dependent_function_name(generator, ctx, "__nac3_ndarray_size"); + + create_and_call_function( + ctx, + &name, + Some(llvm_usize.into()), + &[(llvm_ndarray.into(), ndarray.as_base_value().into())], + Some("size"), + None, + ) + .map(BasicValueEnum::into_int_value) + .unwrap() +} + +pub fn call_nac3_ndarray_nbytes<'ctx, G: CodeGenerator + ?Sized>( + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + ndarray: NDArrayValue<'ctx>, +) -> IntValue<'ctx> { + let llvm_usize = generator.get_size_type(ctx.ctx); + let llvm_ndarray = NDArrayType::llvm_type(ctx.ctx, llvm_usize); + + let name = get_usize_dependent_function_name(generator, ctx, "__nac3_ndarray_nbytes"); + + create_and_call_function( + ctx, + &name, + Some(llvm_usize.into()), + &[(llvm_ndarray.into(), ndarray.as_base_value().into())], + Some("nbytes"), + None, + ) + .map(BasicValueEnum::into_int_value) + .unwrap() +} + +pub fn call_nac3_ndarray_len<'ctx, G: CodeGenerator + ?Sized>( + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + ndarray: NDArrayValue<'ctx>, +) -> IntValue<'ctx> { + let llvm_usize = generator.get_size_type(ctx.ctx); + let llvm_ndarray = NDArrayType::llvm_type(ctx.ctx, llvm_usize); + + let name = get_usize_dependent_function_name(generator, ctx, "__nac3_ndarray_len"); + + create_and_call_function( + ctx, + &name, + Some(llvm_usize.into()), + &[(llvm_ndarray.into(), ndarray.as_base_value().into())], + Some("len"), + None, + ) + .map(BasicValueEnum::into_int_value) + .unwrap() +} + +pub fn call_nac3_ndarray_is_c_contiguous<'ctx, G: CodeGenerator + ?Sized>( + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + ndarray: NDArrayValue<'ctx>, +) -> IntValue<'ctx> { + let llvm_i1 = ctx.ctx.bool_type(); + let llvm_usize = generator.get_size_type(ctx.ctx); + let llvm_ndarray = NDArrayType::llvm_type(ctx.ctx, llvm_usize); + + let name = get_usize_dependent_function_name(generator, ctx, "__nac3_ndarray_is_c_contiguous"); + + create_and_call_function( + ctx, + &name, + Some(llvm_i1.into()), + &[(llvm_ndarray.into(), ndarray.as_base_value().into())], + Some("is_c_contiguous"), + None, + ) + .map(BasicValueEnum::into_int_value) + .unwrap() +} + +pub fn call_nac3_ndarray_get_nth_pelement<'ctx, G: CodeGenerator + ?Sized>( + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + ndarray: NDArrayValue<'ctx>, + index: IntValue<'ctx>, +) -> PointerValue<'ctx> { + let llvm_i8 = ctx.ctx.i8_type(); + let llvm_pi8 = llvm_i8.ptr_type(AddressSpace::default()); + let llvm_usize = generator.get_size_type(ctx.ctx); + let llvm_ndarray = NDArrayType::llvm_type(ctx.ctx, llvm_usize); + + let name = get_usize_dependent_function_name(generator, ctx, "__nac3_ndarray_get_nth_pelement"); + + create_and_call_function( + ctx, + &name, + Some(llvm_pi8.into()), + &[(llvm_ndarray.into(), ndarray.as_base_value().into()), (llvm_usize.into(), index.into())], + Some("pelement"), + None, + ) + .map(BasicValueEnum::into_pointer_value) + .unwrap() +} + +pub fn call_nac3_ndarray_get_pelement_by_indices<'ctx, G: CodeGenerator + ?Sized>( + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + ndarray: NDArrayValue<'ctx>, + indices: PointerValue<'ctx>, +) -> PointerValue<'ctx> { + let llvm_i8 = ctx.ctx.i8_type(); + let llvm_pi8 = llvm_i8.ptr_type(AddressSpace::default()); + let llvm_usize = generator.get_size_type(ctx.ctx); + let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default()); + let llvm_ndarray = NDArrayType::llvm_type(ctx.ctx, llvm_usize); + + let name = + get_usize_dependent_function_name(generator, ctx, "__nac3_ndarray_get_pelement_by_indices"); + + create_and_call_function( + ctx, + &name, + Some(llvm_pi8.into()), + &[ + (llvm_ndarray.into(), ndarray.as_base_value().into()), + (llvm_pusize.into(), indices.into()), + ], + Some("pelement"), + None, + ) + .map(BasicValueEnum::into_pointer_value) + .unwrap() +} + +pub fn call_nac3_ndarray_set_strides_by_shape<'ctx, G: CodeGenerator + ?Sized>( + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + ndarray: NDArrayValue<'ctx>, +) { + let llvm_usize = generator.get_size_type(ctx.ctx); + let llvm_ndarray = NDArrayType::llvm_type(ctx.ctx, llvm_usize); + + let name = + get_usize_dependent_function_name(generator, ctx, "__nac3_ndarray_set_strides_by_shape"); + + create_and_call_function( + ctx, + &name, + None, + &[(llvm_ndarray.into(), ndarray.as_base_value().into())], + None, + None, + ); +} + +pub fn call_nac3_ndarray_copy_data<'ctx, G: CodeGenerator + ?Sized>( + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + src_ndarray: NDArrayValue<'ctx>, + dst_ndarray: NDArrayValue<'ctx>, +) { + let llvm_usize = generator.get_size_type(ctx.ctx); + let llvm_ndarray = NDArrayType::llvm_type(ctx.ctx, llvm_usize); + + let name = get_usize_dependent_function_name(generator, ctx, "__nac3_ndarray_copy_data"); + + create_and_call_function( + ctx, + &name, + None, + &[ + (llvm_ndarray.into(), src_ndarray.as_base_value().into()), + (llvm_ndarray.into(), dst_ndarray.as_base_value().into()), + ], + None, + None, + ); +} diff --git a/nac3core/src/codegen/llvm_intrinsics.rs b/nac3core/src/codegen/llvm_intrinsics.rs index 9c11f88d..f3567514 100644 --- a/nac3core/src/codegen/llvm_intrinsics.rs +++ b/nac3core/src/codegen/llvm_intrinsics.rs @@ -201,6 +201,52 @@ pub fn call_memcpy_generic<'ctx>( call_memcpy(ctx, dest, src, len, is_volatile); } +/// Invokes the `llvm.memcpy` intrinsic. +/// +/// Unlike [`call_memcpy`], this function accepts any type of pointer value. If `dest` or `src` is +/// not a pointer to an integer, the pointer(s) will be cast to `i8*` before invoking `memcpy`. +/// Moreover, `len` now refers to the number of elements (rather than bytes) to copy. +pub fn call_memcpy_generic_array<'ctx>( + ctx: &CodeGenContext<'ctx, '_>, + dest: PointerValue<'ctx>, + src: PointerValue<'ctx>, + len: IntValue<'ctx>, + is_volatile: IntValue<'ctx>, +) { + let llvm_i8 = ctx.ctx.i8_type(); + let llvm_p0i8 = llvm_i8.ptr_type(AddressSpace::default()); + let llvm_sizeof_expr_t = llvm_i8.size_of().get_type(); + + let dest_elem_t = dest.get_type().get_element_type(); + let src_elem_t = src.get_type().get_element_type(); + + let dest = if matches!(dest_elem_t, IntType(t) if t.get_bit_width() == 8) { + dest + } else { + ctx.builder + .build_bit_cast(dest, llvm_p0i8, "") + .map(BasicValueEnum::into_pointer_value) + .unwrap() + }; + let src = if matches!(src_elem_t, IntType(t) if t.get_bit_width() == 8) { + src + } else { + ctx.builder + .build_bit_cast(src, llvm_p0i8, "") + .map(BasicValueEnum::into_pointer_value) + .unwrap() + }; + + let len = ctx.builder.build_int_cast(len, llvm_sizeof_expr_t, "").unwrap(); + let len = ctx.builder.build_int_mul( + len, + src_elem_t.size_of().unwrap(), + "" + ).unwrap(); + + call_memcpy(ctx, dest, src, len, is_volatile); +} + /// Macro to find and generate build call for llvm intrinsic (body of llvm intrinsic function) /// /// Arguments: diff --git a/nac3core/src/codegen/numpy.rs b/nac3core/src/codegen/numpy.rs index e65bc507..d49d5cfa 100644 --- a/nac3core/src/codegen/numpy.rs +++ b/nac3core/src/codegen/numpy.rs @@ -41,6 +41,7 @@ use crate::{ }; /// Creates an uninitialized `NDArray` instance. +#[deprecated = "Use NDArrayType::construct_uninitialized instead."] fn create_ndarray_uninitialized<'ctx, G: CodeGenerator + ?Sized>( generator: &mut G, ctx: &mut CodeGenContext<'ctx, '_>, @@ -84,6 +85,7 @@ where ) -> Result, String>, { let llvm_usize = generator.get_size_type(ctx.ctx); + let llvm_elem_ty = ctx.get_llvm_type(generator, elem_ty); // Assert that all dimensions are non-negative let shape_len = shape_len_fn(generator, ctx, shape)?; @@ -123,10 +125,10 @@ where llvm_usize.const_int(1, false), )?; - let ndarray = create_ndarray_uninitialized(generator, ctx, elem_ty)?; - let num_dims = shape_len_fn(generator, ctx, shape)?; - ndarray.store_ndims(ctx, generator, num_dims); + + let ndarray = NDArrayType::new(generator, ctx.ctx, llvm_elem_ty) + .construct_uninitialized(generator, ctx, num_dims, None); let ndarray_num_dims = ndarray.load_ndims(ctx); ndarray.create_shape(ctx, llvm_usize, ndarray_num_dims); @@ -215,7 +217,9 @@ fn ndarray_init_data<'ctx, G: CodeGenerator + ?Sized>( &ndarray.shape().as_slice_value(ctx, generator), (None, None), ); - ndarray.create_data(generator, ctx, llvm_ndarray_data_t, ndarray_num_elems); + unsafe { + ndarray.create_data(generator, ctx, ndarray_num_elems); + } ndarray } @@ -1262,6 +1266,7 @@ pub fn ndarray_sliced_copy<'ctx, G: CodeGenerator + ?Sized>( ) -> Result, String> { let llvm_i32 = ctx.ctx.i32_type(); let llvm_usize = generator.get_size_type(ctx.ctx); + let llvm_elem_ty = ctx.get_llvm_type(generator, elem_ty); let ndarray = if slices.is_empty() { create_ndarray_dyn_shape( @@ -1275,8 +1280,8 @@ pub fn ndarray_sliced_copy<'ctx, G: CodeGenerator + ?Sized>( }, )? } else { - let ndarray = create_ndarray_uninitialized(generator, ctx, elem_ty)?; - ndarray.store_ndims(ctx, generator, this.load_ndims(ctx)); + let ndarray = NDArrayType::new(generator, ctx.ctx, llvm_elem_ty) + .construct_uninitialized(generator, ctx, this.load_ndims(ctx), None); let ndims = this.load_ndims(ctx); ndarray.create_shape(ctx, llvm_usize, ndims); diff --git a/nac3core/src/codegen/types/ndarray.rs b/nac3core/src/codegen/types/ndarray.rs index c24edb1a..940f0c9c 100644 --- a/nac3core/src/codegen/types/ndarray.rs +++ b/nac3core/src/codegen/types/ndarray.rs @@ -82,7 +82,7 @@ impl<'ctx> NDArrayType<'ctx> { Ok(()) } - // TODO: Move this into e.g. StructProxyType + // TODO: Move this as a member of this Struct #[must_use] fn fields( ctx: impl AsContextRef<'ctx>, @@ -103,7 +103,7 @@ impl<'ctx> NDArrayType<'ctx> { /// Creates an LLVM type corresponding to the expected structure of an `NDArray`. #[must_use] - fn llvm_type(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> PointerType<'ctx> { + pub fn llvm_type(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> PointerType<'ctx> { // struct NDArray { data: i8*, itemsize: size_t, ndims: size_t, shape: size_t*, strides: size_t* } // // * data : Pointer to an array containing the array data @@ -189,22 +189,20 @@ impl<'ctx> NDArrayType<'ctx> { &self, generator: &mut G, ctx: &mut CodeGenContext<'ctx, '_>, - ndims: u64, + // ndims: u64, + ndims: IntValue<'ctx>, name: Option<&'ctx str>, ) -> >::Value { let ndarray = self.new_value(generator, ctx, name); - let itemsize = ctx - .builder - .build_int_z_extend_or_bit_cast(self.dtype.size_of().unwrap(), self.llvm_usize, "") - .unwrap(); + let itemsize = + ctx.builder.build_int_cast(self.dtype.size_of().unwrap(), self.llvm_usize, "").unwrap(); ndarray.store_itemsize(ctx, generator, itemsize); - let ndims_val = self.llvm_usize.const_int(ndims, false); - ndarray.store_ndims(ctx, generator, ndims_val); + ndarray.store_ndims(ctx, generator, ndims); - ndarray.create_shape(ctx, self.llvm_usize, ndims_val); - ndarray.create_strides(ctx, self.llvm_usize, ndims_val); + ndarray.create_shape(ctx, self.llvm_usize, ndims); + ndarray.create_strides(ctx, self.llvm_usize, ndims); ndarray } @@ -220,7 +218,14 @@ impl<'ctx> NDArrayType<'ctx> { shape: &[u64], name: Option<&'ctx str>, ) -> >::Value { - let ndarray = self.construct_uninitialized(generator, ctx, shape.len() as u64, name); + let llvm_usize = generator.get_size_type(ctx.ctx); + + let ndarray = self.construct_uninitialized( + generator, + ctx, + llvm_usize.const_int(shape.len() as u64, false), + name, + ); // Write shape let ndarray_shape = ndarray.shape(); @@ -250,7 +255,14 @@ impl<'ctx> NDArrayType<'ctx> { shape: &[IntValue<'ctx>], name: Option<&'ctx str>, ) -> >::Value { - let ndarray = self.construct_uninitialized(generator, ctx, shape.len() as u64, name); + let llvm_usize = generator.get_size_type(ctx.ctx); + + let ndarray = self.construct_uninitialized( + generator, + ctx, + llvm_usize.const_int(shape.len() as u64, false), + name, + ); // Write shape let ndarray_shape = ndarray.shape(); diff --git a/nac3core/src/codegen/types/structure.rs b/nac3core/src/codegen/types/structure.rs index 444fa2ce..adfc53ab 100644 --- a/nac3core/src/codegen/types/structure.rs +++ b/nac3core/src/codegen/types/structure.rs @@ -145,7 +145,7 @@ where } /// Sets the value of this field for a given `obj`. - pub fn set_from_value(&self, obj: StructValue<'ctx>, value: Value) { + pub fn set_for_value(&self, obj: StructValue<'ctx>, value: Value) { obj.set_field_at_index(self.index, value); } diff --git a/nac3core/src/codegen/values/ndarray.rs b/nac3core/src/codegen/values/ndarray.rs index 9ace252b..50d18122 100644 --- a/nac3core/src/codegen/values/ndarray.rs +++ b/nac3core/src/codegen/values/ndarray.rs @@ -10,7 +10,7 @@ use super::{ }; use crate::codegen::{ irrt, - llvm_intrinsics::call_int_umin, + llvm_intrinsics::{call_int_umin, call_memcpy_generic_array}, stmt::gen_for_callback_incrementing, type_aligned_alloca, types::{structure::StructField, NDArrayType}, @@ -79,7 +79,9 @@ impl<'ctx> NDArrayValue<'ctx> { } fn itemsize(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, IntValue<'ctx>> { - self.get_type().get_fields(ctx.ctx, self.llvm_usize).itemsize + self.get_type() + .get_fields(ctx.ctx, self.llvm_usize) + .itemsize } /// Stores the size of each element `itemsize` into this instance. @@ -179,19 +181,29 @@ impl<'ctx> NDArrayValue<'ctx> { /// Convenience method for creating a new array storing data elements with the given element /// type `elem_ty` and `size`. - pub fn create_data( + /// + /// The data buffer will be allocated on the stack, and is considered to be owned by this ndarray instance. + /// + /// # Safety + /// + /// `shape` and `itemsize` of the ndarray must be initialized. + pub unsafe fn create_data( &self, generator: &mut G, ctx: &mut CodeGenContext<'ctx, '_>, - elem_ty: BasicTypeEnum<'ctx>, size: IntValue<'ctx>, ) { + // let itemsize = + // ctx.builder.build_int_cast(self.load_itemsize(ctx), size.get_type(), "").unwrap(); let itemsize = - ctx.builder.build_int_cast(elem_ty.size_of().unwrap(), size.get_type(), "").unwrap(); + ctx.builder.build_int_cast(self.dtype.size_of().unwrap(), size.get_type(), "").unwrap(); let nbytes = ctx.builder.build_int_mul(size, itemsize, "").unwrap(); + // let nbytes = self.nbytes(generator, ctx); - let data = type_aligned_alloca(generator, ctx, elem_ty, nbytes, None); + let data = type_aligned_alloca(generator, ctx, self.dtype, nbytes, None); self.store_data(ctx, data); + + // self.set_strides_contiguous(generator, ctx); } /// Returns a proxy object to the field storing the data of this `NDArray`. @@ -199,6 +211,133 @@ impl<'ctx> NDArrayValue<'ctx> { pub fn data(&self) -> NDArrayDataProxy<'ctx, '_> { NDArrayDataProxy(self) } + + /// Copy shape dimensions from an array. + pub fn copy_shape_from_array( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + shape: PointerValue<'ctx>, + ) { + let num_items = self.load_ndims(ctx); + + call_memcpy_generic_array( + ctx, + self.shape().base_ptr(ctx, generator), + shape, + num_items, + ctx.ctx.bool_type().const_zero(), + ); + } + + /// Copy shape dimensions from an ndarray. + /// Panics if `ndims` mismatches. + pub fn copy_shape_from_ndarray( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + src_ndarray: NDArrayValue<'ctx>, + ) { + assert_eq!(self.ndims, src_ndarray.ndims); + let src_shape = src_ndarray.shape().base_ptr(ctx, generator); + self.copy_shape_from_array(generator, ctx, src_shape); + } + + /// Copy strides dimensions from an array. + pub fn copy_strides_from_array( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + strides: PointerValue<'ctx>, + ) { + let num_items = self.load_ndims(ctx); + + call_memcpy_generic_array( + ctx, + self.strides().base_ptr(ctx, generator), + strides, + num_items, + ctx.ctx.bool_type().const_zero(), + ); + } + + /// Copy strides dimensions from an ndarray. + /// Panics if `ndims` mismatches. + pub fn copy_strides_from_ndarray( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + src_ndarray: NDArrayValue<'ctx>, + ) { + assert_eq!(self.ndims, src_ndarray.ndims); + let src_strides = src_ndarray.strides().base_ptr(ctx, generator); + self.copy_strides_from_array(generator, ctx, src_strides); + } + + /// Get the `np.size()` of this ndarray. + pub fn size( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + ) -> IntValue<'ctx> { + irrt::ndarray::call_nac3_ndarray_size(generator, ctx, *self) + } + + /// Get the `ndarray.nbytes` of this ndarray. + pub fn nbytes( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + ) -> IntValue<'ctx> { + irrt::ndarray::call_nac3_ndarray_nbytes(generator, ctx, *self) + } + + /// Get the `len()` of this ndarray. + pub fn len( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + ) -> IntValue<'ctx> { + irrt::ndarray::call_nac3_ndarray_len(generator, ctx, *self) + } + + /// Check if this ndarray is C-contiguous. + /// + /// See NumPy's `flags["C_CONTIGUOUS"]`: + pub fn is_c_contiguous( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + ) -> IntValue<'ctx> { + irrt::ndarray::call_nac3_ndarray_is_c_contiguous(generator, ctx, *self) + } + + /// Call [`call_nac3_ndarray_set_strides_by_shape`] on this ndarray to update `strides`. + /// + /// Update the ndarray's strides to make the ndarray contiguous. + pub fn set_strides_contiguous( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + ) { + irrt::ndarray::call_nac3_ndarray_set_strides_by_shape(generator, ctx, *self); + } + + /// Copy data from another ndarray. + /// + /// This ndarray and `src` is that their `np.size()` should be the same. Their shapes + /// do not matter. The copying order is determined by how their flattened views look. + /// + /// Panics if the `dtype`s of ndarrays are different. + pub fn copy_data_from( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + src: NDArrayValue<'ctx>, + ) { + assert_eq!(self.dtype, src.dtype, "self and src dtype should match"); + irrt::ndarray::call_nac3_ndarray_copy_data(generator, ctx, src, *self); + } } impl<'ctx> ProxyValue<'ctx> for NDArrayValue<'ctx> {