core: Add more creator functions for ProxyType

This commit is contained in:
David Mak 2024-06-07 13:00:42 +08:00
parent 181ac3ec1a
commit 210d9e2334
4 changed files with 289 additions and 62 deletions

View File

@ -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<Self::Base> {
/// 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<G: CodeGenerator + ?Sized>(
&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<G: CodeGenerator + ?Sized>(
&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<Self::Base> {
/// 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<G: CodeGenerator + ?Sized>(
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<G: CodeGenerator + ?Sized>(
&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: <Self::Value as ProxyValue<'ctx>>::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<ListType<'ctx>> 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<ListValue<'ctx>> 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<G: CodeGenerator + ?Sized>(
&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: <Self::Value as ProxyValue<'ctx>>::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<RangeType<'ctx>> 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<RangeValue<'ctx>> for PointerValue<'ctx> {
@ -1005,6 +1136,34 @@ impl<'ctx> NDArrayType<'ctx> {
Ok(())
}
/// Creates an instance of [`ListType`].
#[must_use]
pub fn new<G: CodeGenerator + ?Sized>(
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<G: CodeGenerator + ?Sized>(
&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: <Self::Value as ProxyValue<'ctx>>::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<NDArrayType<'ctx>> 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<NDArrayValue<'ctx>> for PointerValue<'ctx> {

View File

@ -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) {

View File

@ -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());
}

View File

@ -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,