diff --git a/nac3core/src/codegen/classes.rs b/nac3core/src/codegen/classes.rs index b2204a2e9..e78ea4a36 100644 --- a/nac3core/src/codegen/classes.rs +++ b/nac3core/src/codegen/classes.rs @@ -1,10 +1,11 @@ use inkwell::{ - IntPredicate, + AddressSpace, IntPredicate, types::{AnyTypeEnum, BasicTypeEnum, IntType, PointerType}, values::{BasicValueEnum, IntValue, PointerValue}, }; -use inkwell::types::BasicType; -use inkwell::values::BasicValue; +use inkwell::context::Context; +use inkwell::types::{ArrayType, BasicType, StructType}; +use inkwell::values::{ArrayValue, BasicValue, StructValue}; use crate::codegen::{ CodeGenContext, CodeGenerator, @@ -14,13 +15,39 @@ use crate::codegen::{ }; /// A LLVM type that is used to represent a non-primitive type in NAC3. -pub trait ProxyType<'ctx> { - /// The underlying type as represented by an LLVM type. +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 underlying LLVM type used to represent values. This is usually the element type of + /// [`Base`] if it is a pointer, otherwise this is the same type as `Base`. + type Underlying: 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> { + generator + .gen_array_var_alloc(ctx, self.as_underlying_type().as_basic_type_enum(), size, name) + .unwrap() + } + /// Creates a [`value`][ProxyValue] with this as its type. fn create_value( &self, @@ -28,23 +55,39 @@ pub trait ProxyType<'ctx> { name: Option<&'ctx str>, ) -> Self::Value; - /// Returns the base type of this proxy. + /// Returns the [base type][Self::Base] of this proxy. fn as_base_type(&self) -> Self::Base; + + /// Returns the [underlying type][Self::Underlying] of this proxy. + fn as_underlying_type(&self) -> Self::Underlying; } /// A LLVM type that is used to represent a non-primitive value in NAC3. -pub trait ProxyValue<'ctx> { - /// The underlying type as represented by an LLVM value. +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 underlying type of LLVM values represented by this instance. This is usually the element + /// type of [`Base`] if it is a pointer, otherwise this is the same type as `Base`. + type Underlying: 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 of this proxy. + /// 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 @@ -463,6 +506,27 @@ impl<'ctx> ListType<'ctx> { 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 { @@ -495,8 +559,21 @@ impl<'ctx> ListType<'ctx> { impl<'ctx> ProxyType<'ctx> for ListType<'ctx> { type Base = PointerType<'ctx>; + type Underlying = StructType<'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_underlying_type().into(), name).unwrap(), + name, + ) + } + fn create_value( &self, value: >::Base, @@ -510,6 +587,10 @@ impl<'ctx> ProxyType<'ctx> for ListType<'ctx> { fn as_base_type(&self) -> Self::Base { self.ty } + + fn as_underlying_type(&self) -> Self::Underlying { + self.as_base_type().get_element_type().into_struct_type() + } } impl<'ctx> From> for PointerType<'ctx> { @@ -629,6 +710,7 @@ impl<'ctx> ListValue<'ctx> { impl<'ctx> ProxyValue<'ctx> for ListValue<'ctx> { type Base = PointerValue<'ctx>; + type Underlying = StructValue<'ctx>; type Type = ListType<'ctx>; fn get_type(&self) -> Self::Type { @@ -638,6 +720,17 @@ impl<'ctx> ProxyValue<'ctx> for ListValue<'ctx> { fn as_base_value(&self) -> Self::Base { self.value } + + fn as_underlying_value( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + name: Option<&'ctx str>, + ) -> Self::Underlying { + ctx.builder + .build_load(self.as_base_value(), name.unwrap_or_default()) + .map(BasicValueEnum::into_struct_value) + .unwrap() + } } impl<'ctx> From> for PointerValue<'ctx> { @@ -757,6 +850,15 @@ impl<'ctx> RangeType<'ctx> { 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 { @@ -778,8 +880,21 @@ impl<'ctx> RangeType<'ctx> { impl<'ctx> ProxyType<'ctx> for RangeType<'ctx> { type Base = PointerType<'ctx>; + type Underlying = ArrayType<'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_underlying_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()); @@ -789,6 +904,10 @@ impl<'ctx> ProxyType<'ctx> for RangeType<'ctx> { fn as_base_type(&self) -> Self::Base { self.ty } + + fn as_underlying_type(&self) -> Self::Underlying { + self.as_base_type().get_element_type().into_array_type() + } } impl<'ctx> From> for PointerType<'ctx> { @@ -935,6 +1054,7 @@ impl<'ctx> RangeValue<'ctx> { impl<'ctx> ProxyValue<'ctx> for RangeValue<'ctx> { type Base = PointerValue<'ctx>; + type Underlying = ArrayValue<'ctx>; type Type = RangeType<'ctx>; fn get_type(&self) -> Self::Type { @@ -944,6 +1064,17 @@ impl<'ctx> ProxyValue<'ctx> for RangeValue<'ctx> { fn as_base_value(&self) -> Self::Base { self.value } + + fn as_underlying_value( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + name: Option<&'ctx str>, + ) -> Self::Underlying { + ctx.builder + .build_load(self.as_base_value(), name.unwrap_or_default()) + .map(BasicValueEnum::into_array_value) + .unwrap() + } } impl<'ctx> From> for PointerValue<'ctx> { @@ -1005,6 +1136,34 @@ impl<'ctx> NDArrayType<'ctx> { 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 { @@ -1037,8 +1196,23 @@ impl<'ctx> NDArrayType<'ctx> { impl<'ctx> ProxyType<'ctx> for NDArrayType<'ctx> { type Base = PointerType<'ctx>; + type Underlying = StructType<'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_underlying_type().into(), name) + .unwrap(), + name, + ) + } + fn create_value( &self, value: >::Base, @@ -1052,6 +1226,10 @@ impl<'ctx> ProxyType<'ctx> for NDArrayType<'ctx> { fn as_base_type(&self) -> Self::Base { self.ty } + + fn as_underlying_type(&self) -> Self::Underlying { + self.as_base_type().get_element_type().into_struct_type() + } } impl<'ctx> From> for PointerType<'ctx> { @@ -1198,6 +1376,7 @@ impl<'ctx> NDArrayValue<'ctx> { impl<'ctx> ProxyValue<'ctx> for NDArrayValue<'ctx> { type Base = PointerValue<'ctx>; + type Underlying = StructValue<'ctx>; type Type = NDArrayType<'ctx>; fn get_type(&self) -> Self::Type { @@ -1207,6 +1386,17 @@ impl<'ctx> ProxyValue<'ctx> for NDArrayValue<'ctx> { fn as_base_value(&self) -> Self::Base { self.value } + + fn as_underlying_value( + &self, + ctx: &mut CodeGenContext<'ctx, '_>, + name: Option<&'ctx str>, + ) -> Self::Underlying { + ctx.builder + .build_load(self.as_base_value(), name.unwrap_or_default()) + .map(BasicValueEnum::into_struct_value) + .unwrap() + } } impl<'ctx> From> for PointerValue<'ctx> { diff --git a/nac3core/src/codegen/mod.rs b/nac3core/src/codegen/mod.rs index 06b24bffd..315725667 100644 --- a/nac3core/src/codegen/mod.rs +++ b/nac3core/src/codegen/mod.rs @@ -1,4 +1,5 @@ use crate::{ + codegen::classes::{ListType, NDArrayType, ProxyType, RangeType}, symbol_resolver::{StaticValue, SymbolResolver}, toplevel::{ helper::PRIMITIVE_DEF_IDS, @@ -452,7 +453,6 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>( } TObj { obj_id, .. } if *obj_id == PRIMITIVE_DEF_IDS.ndarray => { - let llvm_usize = generator.get_size_type(ctx); let (dtype, _) = unpack_ndarray_var_tys(unifier, ty); let element_type = get_llvm_type( ctx, @@ -464,17 +464,7 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>( dtype, ); - // 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 fields = [ - llvm_usize.into(), - llvm_usize.ptr_type(AddressSpace::default()).into(), - element_type.ptr_type(AddressSpace::default()).into(), - ]; - ctx.struct_type(&fields, false).ptr_type(AddressSpace::default()).into() + NDArrayType::new(generator, ctx, element_type).as_base_type().into() } _ => unreachable!("LLVM type for primitive {} is missing", unifier.stringify(ty)), @@ -528,15 +518,11 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>( ctx.struct_type(&fields, false).into() } TList { ty } => { - // a struct with an integer and a pointer to an array let element_type = get_llvm_type( ctx, module, generator, unifier, top_level, type_cache, *ty, ); - let fields = [ - element_type.ptr_type(AddressSpace::default()).into(), - generator.get_size_type(ctx).into(), - ]; - ctx.struct_type(&fields, false).ptr_type(AddressSpace::default()).into() + + ListType::new(generator, ctx, element_type).as_base_type().into() } TVirtual { .. } => unimplemented!(), _ => unreachable!("{}", ty_enum.get_type_name()), @@ -671,7 +657,7 @@ pub fn gen_func_impl<'ctx, G: CodeGenerator, F: FnOnce(&mut G, &mut CodeGenConte Some(t) => t.as_basic_type_enum() } }), - (primitives.range, context.i32_type().array_type(3).ptr_type(AddressSpace::default()).into()), + (primitives.range, RangeType::new(context).as_base_type().into()), (primitives.exception, { let name = "Exception"; if let Some(t) = module.get_struct_type(name) { diff --git a/nac3core/src/codegen/test.rs b/nac3core/src/codegen/test.rs index 1810a18bd..9e7412e5e 100644 --- a/nac3core/src/codegen/test.rs +++ b/nac3core/src/codegen/test.rs @@ -1,6 +1,7 @@ use crate::{ codegen::{ - concrete_type::ConcreteTypeStore, CodeGenContext, CodeGenLLVMOptions, + classes::{ListType, NDArrayType, ProxyType, RangeType}, + concrete_type::ConcreteTypeStore, CodeGenContext, CodeGenerator, CodeGenLLVMOptions, CodeGenTargetMachineOptions, CodeGenTask, DefaultCodeGenerator, WithCall, WorkerRegistry, }, symbol_resolver::{SymbolResolver, ValueEnum}, @@ -425,3 +426,35 @@ fn test_simple_call() { registry.add_task(task); registry.wait_tasks_complete(handles); } + +#[test] +fn test_classes_list_type_new() { + let ctx = inkwell::context::Context::create(); + let generator = DefaultCodeGenerator::new(String::new(), 64); + + let llvm_i32 = ctx.i32_type(); + let llvm_usize = generator.get_size_type(&ctx); + + let llvm_list = ListType::new(&generator, &ctx, llvm_i32.into()); + assert!(ListType::is_type(llvm_list.as_base_type(), llvm_usize).is_ok()); +} + +#[test] +fn test_classes_range_type_new() { + let ctx = inkwell::context::Context::create(); + + let llvm_range = RangeType::new(&ctx); + assert!(RangeType::is_type(llvm_range.as_base_type()).is_ok()); +} + +#[test] +fn test_classes_ndarray_type_new() { + let ctx = inkwell::context::Context::create(); + let generator = DefaultCodeGenerator::new(String::new(), 64); + + let llvm_i32 = ctx.i32_type(); + let llvm_usize = generator.get_size_type(&ctx); + + let llvm_ndarray = NDArrayType::new(&generator, &ctx, llvm_i32.into()); + assert!(NDArrayType::is_type(llvm_ndarray.as_base_type(), llvm_usize).is_ok()); +} diff --git a/nac3core/src/toplevel/builtins.rs b/nac3core/src/toplevel/builtins.rs index 56026170b..18aedb8a3 100644 --- a/nac3core/src/toplevel/builtins.rs +++ b/nac3core/src/toplevel/builtins.rs @@ -12,7 +12,15 @@ use itertools::Either; use crate::{ codegen::{ builtin_fns, - classes::{ArrayLikeValue, NDArrayValue, RangeValue, TypedArrayLikeAccessor}, + classes::{ + ArrayLikeValue, + NDArrayValue, + ProxyType, + ProxyValue, + RangeValue, + RangeType, + TypedArrayLikeAccessor, + }, expr::destructure_range, irrt::*, numpy::*, @@ -943,26 +951,53 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built let mut stop = None; let mut step = None; let int32 = ctx.ctx.i32_type(); - let zero = int32.const_zero(); let ty_i32 = ctx.primitives.int32; for (i, arg) in args.iter().enumerate() { if arg.0 == Some("start".into()) { - start = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?); + start = Some(arg.1 + .clone() + .to_basic_value_enum(ctx, generator, ty_i32)? + .into_int_value() + ); } else if arg.0 == Some("stop".into()) { - stop = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?); + stop = Some( + arg.1 + .clone() + .to_basic_value_enum(ctx, generator, ty_i32)? + .into_int_value() + ); } else if arg.0 == Some("step".into()) { - step = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?); + step = Some( + arg.1 + .clone() + .to_basic_value_enum(ctx, generator, ty_i32)? + .into_int_value() + ); } else if i == 0 { - start = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?); + start = Some( + arg.1 + .clone() + .to_basic_value_enum(ctx, generator, ty_i32)? + .into_int_value() + ); } else if i == 1 { - stop = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?); + stop = Some( + arg.1 + .clone() + .to_basic_value_enum(ctx, generator, ty_i32)? + .into_int_value() + ); } else if i == 2 { - step = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?); + step = Some( + arg.1 + .clone() + .to_basic_value_enum(ctx, generator, ty_i32)? + .into_int_value() + ); } } let step = match step { Some(step) => { - let step = step.into_int_value(); // assert step != 0, throw exception if not let not_zero = ctx.builder .build_int_compare( @@ -989,30 +1024,13 @@ pub fn get_builtins(unifier: &mut Unifier, primitives: &PrimitiveStore) -> Built start = None; v }); - let start = start.unwrap_or_else(|| int32.const_zero().into()); - let ty = int32.array_type(3); - let ptr = generator.gen_var_alloc(ctx, ty.into(), Some("range")).unwrap(); - unsafe { - let a = ctx.builder - .build_in_bounds_gep(ptr, &[zero, zero], "start") - .unwrap(); - let b = ctx.builder - .build_in_bounds_gep( - ptr, - &[zero, int32.const_int(1, false)], - "end", - ) - .unwrap(); - let c = ctx.builder.build_in_bounds_gep( - ptr, - &[zero, int32.const_int(2, false)], - "step", - ).unwrap(); - ctx.builder.build_store(a, start).unwrap(); - ctx.builder.build_store(b, stop).unwrap(); - ctx.builder.build_store(c, step).unwrap(); - } - Ok(Some(ptr.into())) + let start = start.unwrap_or_else(|| int32.const_zero()); + + let ptr = RangeType::new(ctx.ctx).new_value(generator, ctx, Some("range")); + ptr.store_start(ctx, start); + ptr.store_end(ctx, stop); + ptr.store_step(ctx, step); + Ok(Some(ptr.as_base_value().into())) }, )))), loc: None,