forked from M-Labs/nac3
Compare commits
5 Commits
master
...
staging/nd
Author | SHA1 | Date | |
---|---|---|---|
e70805eeaa | |||
28aaafb38e | |||
90043f8ede | |||
2352462143 | |||
e93bae272e |
@ -1,6 +1,6 @@
|
||||
use inkwell::{
|
||||
types::BasicTypeEnum,
|
||||
values::{BasicValueEnum, IntValue, PointerValue},
|
||||
values::{BasicValue, BasicValueEnum, IntValue, PointerValue},
|
||||
FloatPredicate, IntPredicate, OptimizationLevel,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
@ -14,7 +14,7 @@ use super::{
|
||||
numpy,
|
||||
numpy::ndarray_elementwise_unaryop_impl,
|
||||
stmt::gen_for_callback_incrementing,
|
||||
types::ndarray::NDArrayType,
|
||||
types::{ndarray::NDArrayType, TupleType},
|
||||
values::{
|
||||
ndarray::NDArrayValue, ArrayLikeValue, ProxyValue, RangeValue, TypedArrayLikeAccessor,
|
||||
UntypedArrayLikeAccessor,
|
||||
@ -1861,6 +1861,7 @@ pub fn call_numpy_nextafter<'ctx, G: CodeGenerator + ?Sized>(
|
||||
}
|
||||
|
||||
/// Allocates a struct with the fields specified by `out_matrices` and returns a pointer to it
|
||||
#[deprecated = "Use TupleType::construct_from_objects instead."]
|
||||
fn build_output_struct<'ctx>(
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
out_matrices: &[BasicValueEnum<'ctx>],
|
||||
@ -1965,10 +1966,11 @@ pub fn call_np_linalg_qr<'ctx, G: CodeGenerator + ?Sized>(
|
||||
None,
|
||||
);
|
||||
|
||||
let q = q.as_base_value().into();
|
||||
let r = r.as_base_value().into();
|
||||
let out_ptr = build_output_struct(ctx, &[q, r]);
|
||||
Ok(ctx.builder.build_load(out_ptr, "QR_Factorization_result").map(Into::into).unwrap())
|
||||
let q = q.as_base_value().as_basic_value_enum();
|
||||
let r = r.as_base_value().as_basic_value_enum();
|
||||
let tuple = TupleType::new(generator, ctx.ctx, &[q.get_type(), r.get_type()])
|
||||
.construct_from_objects(ctx, [q, r], None);
|
||||
Ok(tuple.as_base_value().into())
|
||||
}
|
||||
|
||||
/// Invokes the `np_linalg_svd` linalg function
|
||||
@ -2023,12 +2025,12 @@ pub fn call_np_linalg_svd<'ctx, G: CodeGenerator + ?Sized>(
|
||||
None,
|
||||
);
|
||||
|
||||
let u = u.as_base_value().into();
|
||||
let s = s.as_base_value().into();
|
||||
let vh = vh.as_base_value().into();
|
||||
let out_ptr = build_output_struct(ctx, &[u, s, vh]);
|
||||
|
||||
Ok(ctx.builder.build_load(out_ptr, "SVD_Factorization_result").map(Into::into).unwrap())
|
||||
let u = u.as_base_value().as_basic_value_enum();
|
||||
let s = s.as_base_value().as_basic_value_enum();
|
||||
let vh = vh.as_base_value().as_basic_value_enum();
|
||||
let tuple = TupleType::new(generator, ctx.ctx, &[u.get_type(), s.get_type(), vh.get_type()])
|
||||
.construct_from_objects(ctx, [u, s, vh], None);
|
||||
Ok(tuple.as_base_value().into())
|
||||
}
|
||||
|
||||
/// Invokes the `np_linalg_inv` linalg function
|
||||
@ -2150,10 +2152,11 @@ pub fn call_sp_linalg_lu<'ctx, G: CodeGenerator + ?Sized>(
|
||||
None,
|
||||
);
|
||||
|
||||
let l = l.as_base_value().into();
|
||||
let u = u.as_base_value().into();
|
||||
let out_ptr = build_output_struct(ctx, &[l, u]);
|
||||
Ok(ctx.builder.build_load(out_ptr, "LU_Factorization_result").map(Into::into).unwrap())
|
||||
let l = l.as_base_value().as_basic_value_enum();
|
||||
let u = u.as_base_value().as_basic_value_enum();
|
||||
let tuple = TupleType::new(generator, ctx.ctx, &[l.get_type(), u.get_type()])
|
||||
.construct_from_objects(ctx, [l, u], None);
|
||||
Ok(tuple.as_base_value().into())
|
||||
}
|
||||
|
||||
/// Invokes the `np_linalg_matrix_power` linalg function
|
||||
@ -2285,10 +2288,11 @@ pub fn call_sp_linalg_schur<'ctx, G: CodeGenerator + ?Sized>(
|
||||
None,
|
||||
);
|
||||
|
||||
let t = t.as_base_value().into();
|
||||
let z = z.as_base_value().into();
|
||||
let out_ptr = build_output_struct(ctx, &[t, z]);
|
||||
Ok(ctx.builder.build_load(out_ptr, "Schur_Factorization_result").map(Into::into).unwrap())
|
||||
let t = t.as_base_value().as_basic_value_enum();
|
||||
let z = z.as_base_value().as_basic_value_enum();
|
||||
let tuple = TupleType::new(generator, ctx.ctx, &[t.get_type(), z.get_type()])
|
||||
.construct_from_objects(ctx, [t, z], None);
|
||||
Ok(tuple.as_base_value().into())
|
||||
}
|
||||
|
||||
/// Invokes the `sp_linalg_hessenberg` linalg function
|
||||
@ -2329,8 +2333,9 @@ pub fn call_sp_linalg_hessenberg<'ctx, G: CodeGenerator + ?Sized>(
|
||||
None,
|
||||
);
|
||||
|
||||
let h = h.as_base_value().into();
|
||||
let q = q.as_base_value().into();
|
||||
let out_ptr = build_output_struct(ctx, &[h, q]);
|
||||
Ok(ctx.builder.build_load(out_ptr, "Hessenberg_decomposition_result").map(Into::into).unwrap())
|
||||
let h = h.as_base_value().as_basic_value_enum();
|
||||
let q = q.as_base_value().as_basic_value_enum();
|
||||
let tuple = TupleType::new(generator, ctx.ctx, &[h.get_type(), q.get_type()])
|
||||
.construct_from_objects(ctx, [h, q], None);
|
||||
Ok(tuple.as_base_value().into())
|
||||
}
|
||||
|
@ -1101,6 +1101,7 @@ pub fn destructure_range<'ctx>(
|
||||
/// Setting `ty` to [`None`] implies that the list is empty **and** does not have a known element
|
||||
/// type, and will therefore set the `list.data` type as `size_t*`. It is undefined behavior to
|
||||
/// generate a sized list with an unknown element type.
|
||||
#[deprecated = "Use ListType::construct instead."]
|
||||
pub fn allocate_list<'ctx, G: CodeGenerator + ?Sized>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
@ -1108,18 +1109,13 @@ pub fn allocate_list<'ctx, G: CodeGenerator + ?Sized>(
|
||||
length: IntValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> ListValue<'ctx> {
|
||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
||||
let llvm_elem_ty = ty.unwrap_or(llvm_usize.into());
|
||||
let list_ty = if let Some(ty) = ty {
|
||||
ListType::new(generator, ctx.ctx, ty)
|
||||
} else {
|
||||
ListType::new_untyped(generator, ctx.ctx)
|
||||
};
|
||||
|
||||
// List structure; type { ty*, size_t }
|
||||
let arr_ty = ListType::new(generator, ctx.ctx, llvm_elem_ty);
|
||||
let list = arr_ty.alloca(generator, ctx, name);
|
||||
|
||||
let length = ctx.builder.build_int_z_extend(length, llvm_usize, "").unwrap();
|
||||
list.store_size(ctx, generator, length);
|
||||
list.create_data(ctx, llvm_elem_ty, None);
|
||||
|
||||
list
|
||||
list_ty.construct(generator, ctx, length, name)
|
||||
}
|
||||
|
||||
/// Generates LLVM IR for a [list comprehension expression][expr].
|
||||
@ -1194,12 +1190,11 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
|
||||
"listcomp.alloc_size",
|
||||
)
|
||||
.unwrap();
|
||||
list = allocate_list(
|
||||
list = ListType::new(generator, ctx.ctx, elem_ty).construct(
|
||||
generator,
|
||||
ctx,
|
||||
Some(elem_ty),
|
||||
list_alloc_size.into_int_value(),
|
||||
Some("listcomp.addr"),
|
||||
Some("listcomp"),
|
||||
);
|
||||
|
||||
let i = generator.gen_store_target(ctx, target, Some("i.addr"))?.unwrap();
|
||||
@ -1246,7 +1241,12 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
|
||||
Some("length"),
|
||||
)
|
||||
.into_int_value();
|
||||
list = allocate_list(generator, ctx, Some(elem_ty), length, Some("listcomp"));
|
||||
list = ListType::new(generator, ctx.ctx, elem_ty).construct(
|
||||
generator,
|
||||
ctx,
|
||||
length,
|
||||
Some("listcomp"),
|
||||
);
|
||||
|
||||
let counter = generator.gen_var_alloc(ctx, size_t.into(), Some("counter.addr"))?;
|
||||
// counter = -1
|
||||
@ -1411,7 +1411,8 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
.build_int_add(lhs.load_size(ctx, None), rhs.load_size(ctx, None), "")
|
||||
.unwrap();
|
||||
|
||||
let new_list = allocate_list(generator, ctx, Some(llvm_elem_ty), size, None);
|
||||
let new_list = ListType::new(generator, ctx.ctx, llvm_elem_ty)
|
||||
.construct(generator, ctx, size, None);
|
||||
|
||||
let lhs_size = ctx
|
||||
.builder
|
||||
@ -1498,10 +1499,9 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
let elem_llvm_ty = ctx.get_llvm_type(generator, elem_ty);
|
||||
let sizeof_elem = elem_llvm_ty.size_of().unwrap();
|
||||
|
||||
let new_list = allocate_list(
|
||||
let new_list = ListType::new(generator, ctx.ctx, elem_llvm_ty).construct(
|
||||
generator,
|
||||
ctx,
|
||||
Some(elem_llvm_ty),
|
||||
ctx.builder.build_int_mul(list_val.load_size(ctx, None), int_val, "").unwrap(),
|
||||
None,
|
||||
);
|
||||
@ -2939,7 +2939,20 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||
Some(elements[0].get_type())
|
||||
};
|
||||
let length = generator.get_size_type(ctx.ctx).const_int(elements.len() as u64, false);
|
||||
let arr_str_ptr = allocate_list(generator, ctx, ty, length, Some("list"));
|
||||
let arr_str_ptr = if let Some(ty) = ty {
|
||||
ListType::new(generator, ctx.ctx, ty).construct(
|
||||
generator,
|
||||
ctx,
|
||||
length,
|
||||
Some("list"),
|
||||
)
|
||||
} else {
|
||||
ListType::new_untyped(generator, ctx.ctx).construct_empty(
|
||||
generator,
|
||||
ctx,
|
||||
Some("list"),
|
||||
)
|
||||
};
|
||||
let arr_ptr = arr_str_ptr.data();
|
||||
for (i, v) in elements.iter().enumerate() {
|
||||
let elem_ptr = arr_ptr.ptr_offset(
|
||||
@ -3417,8 +3430,12 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||
.unwrap(),
|
||||
step,
|
||||
);
|
||||
let res_array_ret =
|
||||
allocate_list(generator, ctx, Some(ty), length, Some("ret"));
|
||||
let res_array_ret = ListType::new(generator, ctx.ctx, ty).construct(
|
||||
generator,
|
||||
ctx,
|
||||
length,
|
||||
Some("ret"),
|
||||
);
|
||||
let Some(res_ind) = handle_slice_indices(
|
||||
&None,
|
||||
&None,
|
||||
|
@ -42,7 +42,7 @@ use crate::{
|
||||
};
|
||||
use concrete_type::{ConcreteType, ConcreteTypeEnum, ConcreteTypeStore};
|
||||
pub use generator::{CodeGenerator, DefaultCodeGenerator};
|
||||
use types::{ndarray::NDArrayType, ListType, ProxyType, RangeType};
|
||||
use types::{ndarray::NDArrayType, ListType, ProxyType, RangeType, TupleType};
|
||||
|
||||
pub mod builtin_fns;
|
||||
pub mod concrete_type;
|
||||
@ -574,7 +574,7 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
|
||||
get_llvm_type(ctx, module, generator, unifier, top_level, type_cache, *ty)
|
||||
})
|
||||
.collect_vec();
|
||||
ctx.struct_type(&fields, false).into()
|
||||
TupleType::new(generator, ctx, &fields).as_base_type().into()
|
||||
}
|
||||
TVirtual { .. } => unimplemented!(),
|
||||
_ => unreachable!("{}", ty_enum.get_type_name()),
|
||||
|
@ -1,10 +1,10 @@
|
||||
use inkwell::{
|
||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, PointerType},
|
||||
types::{BasicType, BasicTypeEnum, PointerType},
|
||||
values::{BasicValue, BasicValueEnum, IntValue, PointerValue},
|
||||
AddressSpace, IntPredicate, OptimizationLevel,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
use nac3core::codegen::values::ndarray::shape::parse_numpy_int_sequence;
|
||||
use nac3parser::ast::{Operator, StrRef};
|
||||
|
||||
use super::{
|
||||
@ -19,11 +19,12 @@ use super::{
|
||||
llvm_intrinsics::{self, call_memcpy_generic},
|
||||
macros::codegen_unreachable,
|
||||
stmt::{gen_for_callback_incrementing, gen_for_range_callback, gen_if_else_expr_callback},
|
||||
types::{ndarray::NDArrayType, ListType, ProxyType},
|
||||
types::{ndarray::{factory::{ndarray_one_value, ndarray_zero_value}, NDArrayType}, ListType, ProxyType},
|
||||
values::{
|
||||
ndarray::NDArrayValue, ArrayLikeIndexer, ArrayLikeValue, ListValue, ProxyValue,
|
||||
TypedArrayLikeAccessor, TypedArrayLikeAdapter, TypedArrayLikeMutator,
|
||||
UntypedArrayLikeAccessor, UntypedArrayLikeMutator,
|
||||
ndarray::NDArrayValue,
|
||||
ArrayLikeIndexer, ArrayLikeValue, ListValue, ProxyValue, TypedArrayLikeAccessor,
|
||||
TypedArrayLikeAdapter, TypedArrayLikeMutator, UntypedArrayLikeAccessor,
|
||||
UntypedArrayLikeMutator,
|
||||
},
|
||||
CodeGenContext, CodeGenerator,
|
||||
};
|
||||
@ -35,6 +36,7 @@ use crate::{
|
||||
typedef::{FunSignature, Type, TypeEnum},
|
||||
},
|
||||
};
|
||||
use crate::toplevel::helper::extract_ndims;
|
||||
|
||||
/// Creates an `NDArray` instance from a dynamic shape.
|
||||
///
|
||||
@ -174,60 +176,6 @@ pub fn create_ndarray_const_shape<'ctx, G: CodeGenerator + ?Sized>(
|
||||
Ok(ndarray)
|
||||
}
|
||||
|
||||
fn ndarray_zero_value<'ctx, G: CodeGenerator + ?Sized>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
elem_ty: Type,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
if [ctx.primitives.int32, ctx.primitives.uint32]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(elem_ty, *ty))
|
||||
{
|
||||
ctx.ctx.i32_type().const_zero().into()
|
||||
} else if [ctx.primitives.int64, ctx.primitives.uint64]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(elem_ty, *ty))
|
||||
{
|
||||
ctx.ctx.i64_type().const_zero().into()
|
||||
} else if ctx.unifier.unioned(elem_ty, ctx.primitives.float) {
|
||||
ctx.ctx.f64_type().const_zero().into()
|
||||
} else if ctx.unifier.unioned(elem_ty, ctx.primitives.bool) {
|
||||
ctx.ctx.bool_type().const_zero().into()
|
||||
} else if ctx.unifier.unioned(elem_ty, ctx.primitives.str) {
|
||||
ctx.gen_string(generator, "").into()
|
||||
} else {
|
||||
codegen_unreachable!(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
fn ndarray_one_value<'ctx, G: CodeGenerator + ?Sized>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
elem_ty: Type,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
if [ctx.primitives.int32, ctx.primitives.uint32]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(elem_ty, *ty))
|
||||
{
|
||||
let is_signed = ctx.unifier.unioned(elem_ty, ctx.primitives.int32);
|
||||
ctx.ctx.i32_type().const_int(1, is_signed).into()
|
||||
} else if [ctx.primitives.int64, ctx.primitives.uint64]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(elem_ty, *ty))
|
||||
{
|
||||
let is_signed = ctx.unifier.unioned(elem_ty, ctx.primitives.int64);
|
||||
ctx.ctx.i64_type().const_int(1, is_signed).into()
|
||||
} else if ctx.unifier.unioned(elem_ty, ctx.primitives.float) {
|
||||
ctx.ctx.f64_type().const_float(1.0).into()
|
||||
} else if ctx.unifier.unioned(elem_ty, ctx.primitives.bool) {
|
||||
ctx.ctx.bool_type().const_int(1, false).into()
|
||||
} else if ctx.unifier.unioned(elem_ty, ctx.primitives.str) {
|
||||
ctx.gen_string(generator, "1").into()
|
||||
} else {
|
||||
codegen_unreachable!(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
/// LLVM-typed implementation for generating the implementation for constructing an `NDArray`.
|
||||
///
|
||||
/// * `elem_ty` - The element type of the `NDArray`.
|
||||
@ -584,17 +532,17 @@ fn llvm_ndlist_get_ndims<'ctx, G: CodeGenerator + ?Sized>(
|
||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
||||
|
||||
let list_ty = ListType::from_type(ty, llvm_usize);
|
||||
let list_elem_ty = list_ty.element_type();
|
||||
let list_elem_ty = list_ty.element_type().unwrap();
|
||||
|
||||
let ndims = llvm_usize.const_int(1, false);
|
||||
match list_elem_ty {
|
||||
AnyTypeEnum::PointerType(ptr_ty)
|
||||
BasicTypeEnum::PointerType(ptr_ty)
|
||||
if ListType::is_representable(ptr_ty, llvm_usize).is_ok() =>
|
||||
{
|
||||
ndims.const_add(llvm_ndlist_get_ndims(generator, ctx, ptr_ty))
|
||||
}
|
||||
|
||||
AnyTypeEnum::PointerType(ptr_ty)
|
||||
BasicTypeEnum::PointerType(ptr_ty)
|
||||
if NDArrayType::is_representable(ptr_ty, llvm_usize).is_ok() =>
|
||||
{
|
||||
todo!("Getting ndims for list[ndarray] not supported")
|
||||
@ -638,10 +586,10 @@ fn ndarray_from_ndlist_impl<'ctx, G: CodeGenerator + ?Sized>(
|
||||
let llvm_i1 = ctx.ctx.bool_type();
|
||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
||||
|
||||
let list_elem_ty = src_lst.get_type().element_type();
|
||||
let list_elem_ty = src_lst.get_type().element_type().unwrap();
|
||||
|
||||
match list_elem_ty {
|
||||
AnyTypeEnum::PointerType(ptr_ty)
|
||||
BasicTypeEnum::PointerType(ptr_ty)
|
||||
if ListType::is_representable(ptr_ty, llvm_usize).is_ok() =>
|
||||
{
|
||||
// The stride of elements in this dimension, i.e. the number of elements between arr[i]
|
||||
@ -701,7 +649,7 @@ fn ndarray_from_ndlist_impl<'ctx, G: CodeGenerator + ?Sized>(
|
||||
)?;
|
||||
}
|
||||
|
||||
AnyTypeEnum::PointerType(ptr_ty)
|
||||
BasicTypeEnum::PointerType(ptr_ty)
|
||||
if NDArrayType::is_representable(ptr_ty, llvm_usize).is_ok() =>
|
||||
{
|
||||
todo!("Not implemented for list[ndarray]")
|
||||
@ -1723,8 +1671,19 @@ pub fn gen_ndarray_empty<'ctx>(
|
||||
let shape_ty = fun.0.args[0].ty;
|
||||
let shape_arg = args[0].1.clone().to_basic_value_enum(context, generator, shape_ty)?;
|
||||
|
||||
call_ndarray_empty_impl(generator, context, context.primitives.float, shape_arg)
|
||||
.map(NDArrayValue::into)
|
||||
let (dtype, ndims) = unpack_ndarray_var_tys(&mut context.unifier, fun.0.ret);
|
||||
let llvm_dtype = context.get_llvm_type(generator, dtype);
|
||||
let ndims = extract_ndims(&context.unifier, ndims);
|
||||
|
||||
let shape = parse_numpy_int_sequence(
|
||||
generator,
|
||||
context,
|
||||
(shape_ty, shape_arg),
|
||||
);
|
||||
|
||||
let ndarray = NDArrayType::new(generator, context.ctx, llvm_dtype, Some(ndims))
|
||||
.construct_numpy_empty(generator, context, shape);
|
||||
Ok(ndarray.as_base_value())
|
||||
}
|
||||
|
||||
/// Generates LLVM IR for `ndarray.zeros`.
|
||||
@ -1741,8 +1700,19 @@ pub fn gen_ndarray_zeros<'ctx>(
|
||||
let shape_ty = fun.0.args[0].ty;
|
||||
let shape_arg = args[0].1.clone().to_basic_value_enum(context, generator, shape_ty)?;
|
||||
|
||||
call_ndarray_zeros_impl(generator, context, context.primitives.float, shape_arg)
|
||||
.map(NDArrayValue::into)
|
||||
let (dtype, ndims) = unpack_ndarray_var_tys(&mut context.unifier, fun.0.ret);
|
||||
let llvm_dtype = context.get_llvm_type(generator, dtype);
|
||||
let ndims = extract_ndims(&context.unifier, ndims);
|
||||
|
||||
let shape = parse_numpy_int_sequence(
|
||||
generator,
|
||||
context,
|
||||
(shape_ty, shape_arg),
|
||||
);
|
||||
|
||||
let ndarray = NDArrayType::new(generator, context.ctx, llvm_dtype, Some(ndims))
|
||||
.construct_numpy_zeros(generator, context, dtype, shape);
|
||||
Ok(ndarray.as_base_value())
|
||||
}
|
||||
|
||||
/// Generates LLVM IR for `ndarray.ones`.
|
||||
@ -1759,8 +1729,19 @@ pub fn gen_ndarray_ones<'ctx>(
|
||||
let shape_ty = fun.0.args[0].ty;
|
||||
let shape_arg = args[0].1.clone().to_basic_value_enum(context, generator, shape_ty)?;
|
||||
|
||||
call_ndarray_ones_impl(generator, context, context.primitives.float, shape_arg)
|
||||
.map(NDArrayValue::into)
|
||||
let (dtype, ndims) = unpack_ndarray_var_tys(&mut context.unifier, fun.0.ret);
|
||||
let llvm_dtype = context.get_llvm_type(generator, dtype);
|
||||
let ndims = extract_ndims(&context.unifier, ndims);
|
||||
|
||||
let shape = parse_numpy_int_sequence(
|
||||
generator,
|
||||
context,
|
||||
(shape_ty, shape_arg),
|
||||
);
|
||||
|
||||
let ndarray = NDArrayType::new(generator, context.ctx, llvm_dtype, Some(ndims))
|
||||
.construct_numpy_ones(generator, context, dtype, shape);
|
||||
Ok(ndarray.as_base_value())
|
||||
}
|
||||
|
||||
/// Generates LLVM IR for `ndarray.full`.
|
||||
@ -1780,8 +1761,19 @@ pub fn gen_ndarray_full<'ctx>(
|
||||
let fill_value_arg =
|
||||
args[1].1.clone().to_basic_value_enum(context, generator, fill_value_ty)?;
|
||||
|
||||
call_ndarray_full_impl(generator, context, fill_value_ty, shape_arg, fill_value_arg)
|
||||
.map(NDArrayValue::into)
|
||||
let (dtype, ndims) = unpack_ndarray_var_tys(&mut context.unifier, fun.0.ret);
|
||||
let llvm_dtype = context.get_llvm_type(generator, dtype);
|
||||
let ndims = extract_ndims(&context.unifier, ndims);
|
||||
|
||||
let shape = parse_numpy_int_sequence(
|
||||
generator,
|
||||
context,
|
||||
(shape_ty, shape_arg),
|
||||
);
|
||||
|
||||
let ndarray = NDArrayType::new(generator, context.ctx, llvm_dtype, Some(ndims))
|
||||
.construct_numpy_full(generator, context, shape, fill_value_arg);
|
||||
Ok(ndarray.as_base_value())
|
||||
}
|
||||
|
||||
pub fn gen_ndarray_array<'ctx>(
|
||||
|
@ -1,69 +1,113 @@
|
||||
use inkwell::{
|
||||
context::Context,
|
||||
context::{AsContextRef, Context},
|
||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
|
||||
values::IntValue,
|
||||
AddressSpace,
|
||||
values::{IntValue, PointerValue},
|
||||
AddressSpace, IntPredicate, OptimizationLevel,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
use nac3core_derive::StructFields;
|
||||
|
||||
use super::ProxyType;
|
||||
use crate::codegen::{
|
||||
values::{ArraySliceValue, ListValue, ProxyValue},
|
||||
CodeGenContext, CodeGenerator,
|
||||
use crate::{
|
||||
codegen::{
|
||||
types::structure::{
|
||||
check_struct_type_matches_fields, FieldIndexCounter, StructField, StructFields,
|
||||
},
|
||||
values::{ArraySliceValue, ListValue, ProxyValue},
|
||||
CodeGenContext, CodeGenerator,
|
||||
},
|
||||
typecheck::typedef::{iter_type_vars, Type, TypeEnum},
|
||||
};
|
||||
|
||||
/// Proxy type for a `list` type in LLVM.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub struct ListType<'ctx> {
|
||||
ty: PointerType<'ctx>,
|
||||
item: Option<BasicTypeEnum<'ctx>>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, StructFields)]
|
||||
pub struct ListStructFields<'ctx> {
|
||||
/// Array pointer to content.
|
||||
#[value_type(i8_type().ptr_type(AddressSpace::default()))]
|
||||
pub items: StructField<'ctx, PointerValue<'ctx>>,
|
||||
|
||||
/// Number of items in the array.
|
||||
#[value_type(usize)]
|
||||
pub len: StructField<'ctx, IntValue<'ctx>>,
|
||||
}
|
||||
|
||||
impl<'ctx> ListStructFields<'ctx> {
|
||||
#[must_use]
|
||||
pub fn new_typed(item: BasicTypeEnum<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
let mut counter = FieldIndexCounter::default();
|
||||
|
||||
ListStructFields {
|
||||
items: StructField::create(
|
||||
&mut counter,
|
||||
"items",
|
||||
item.ptr_type(AddressSpace::default()),
|
||||
),
|
||||
len: StructField::create(&mut counter, "len", llvm_usize),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> ListType<'ctx> {
|
||||
/// Checks whether `llvm_ty` represents a `list` type, returning [Err] if it does not.
|
||||
pub fn is_representable(
|
||||
llvm_ty: PointerType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
let llvm_list_ty = llvm_ty.get_element_type();
|
||||
let AnyTypeEnum::StructType(llvm_list_ty) = llvm_list_ty else {
|
||||
return Err(format!("Expected struct type for `list` type, got {llvm_list_ty}"));
|
||||
};
|
||||
if llvm_list_ty.count_fields() != 2 {
|
||||
return Err(format!(
|
||||
"Expected 2 fields in `list`, got {}",
|
||||
llvm_list_ty.count_fields()
|
||||
));
|
||||
}
|
||||
let ctx = llvm_ty.get_context();
|
||||
|
||||
let list_size_ty = llvm_list_ty.get_field_type_at_index(0).unwrap();
|
||||
let Ok(_) = PointerType::try_from(list_size_ty) else {
|
||||
return Err(format!("Expected pointer type for `list.0`, got {list_size_ty}"));
|
||||
let llvm_ty = llvm_ty.get_element_type();
|
||||
let AnyTypeEnum::StructType(llvm_ty) = llvm_ty else {
|
||||
return Err(format!("Expected struct type for `list` type, got {llvm_ty}"));
|
||||
};
|
||||
|
||||
let list_data_ty = llvm_list_ty.get_field_type_at_index(1).unwrap();
|
||||
let Ok(list_data_ty) = IntType::try_from(list_data_ty) else {
|
||||
return Err(format!("Expected int type for `list.1`, got {list_data_ty}"));
|
||||
};
|
||||
if list_data_ty.get_bit_width() != llvm_usize.get_bit_width() {
|
||||
return Err(format!(
|
||||
"Expected {}-bit int type for `list.1`, got {}-bit int",
|
||||
llvm_usize.get_bit_width(),
|
||||
list_data_ty.get_bit_width()
|
||||
));
|
||||
}
|
||||
let fields = ListStructFields::new(ctx, llvm_usize);
|
||||
|
||||
Ok(())
|
||||
check_struct_type_matches_fields(
|
||||
fields,
|
||||
llvm_ty,
|
||||
"list",
|
||||
&[(fields.items.name(), &|ty| {
|
||||
if ty.is_pointer_type() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Expected T* for `list.items`, got {ty}"))
|
||||
}
|
||||
})],
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns an instance of [`StructFields`] containing all field accessors for this type.
|
||||
#[must_use]
|
||||
fn fields(item: BasicTypeEnum<'ctx>, llvm_usize: IntType<'ctx>) -> ListStructFields<'ctx> {
|
||||
ListStructFields::new_typed(item, llvm_usize)
|
||||
}
|
||||
|
||||
/// See [`ListType::fields`].
|
||||
// TODO: Move this into e.g. StructProxyType
|
||||
#[must_use]
|
||||
pub fn get_fields(&self, _ctx: &impl AsContextRef<'ctx>) -> ListStructFields<'ctx> {
|
||||
Self::fields(self.item.unwrap_or(self.llvm_usize.into()), self.llvm_usize)
|
||||
}
|
||||
|
||||
/// Creates an LLVM type corresponding to the expected structure of a `List`.
|
||||
#[must_use]
|
||||
fn llvm_type(
|
||||
ctx: &'ctx Context,
|
||||
element_type: BasicTypeEnum<'ctx>,
|
||||
element_type: Option<BasicTypeEnum<'ctx>>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> PointerType<'ctx> {
|
||||
// struct List { data: T*, size: size_t }
|
||||
let field_tys = [element_type.ptr_type(AddressSpace::default()).into(), llvm_usize.into()];
|
||||
let element_type = element_type.unwrap_or(llvm_usize.into());
|
||||
|
||||
let field_tys =
|
||||
Self::fields(element_type, llvm_usize).into_iter().map(|field| field.1).collect_vec();
|
||||
|
||||
ctx.struct_type(&field_tys, false).ptr_type(AddressSpace::default())
|
||||
}
|
||||
@ -76,9 +120,50 @@ impl<'ctx> ListType<'ctx> {
|
||||
element_type: BasicTypeEnum<'ctx>,
|
||||
) -> Self {
|
||||
let llvm_usize = generator.get_size_type(ctx);
|
||||
let llvm_list = Self::llvm_type(ctx, element_type, llvm_usize);
|
||||
let llvm_list = Self::llvm_type(ctx, Some(element_type), llvm_usize);
|
||||
|
||||
ListType::from_type(llvm_list, llvm_usize)
|
||||
Self { ty: llvm_list, item: Some(element_type), llvm_usize }
|
||||
}
|
||||
|
||||
/// Creates an instance of [`ListType`] with an unknown element type.
|
||||
#[must_use]
|
||||
pub fn new_untyped<G: CodeGenerator + ?Sized>(generator: &G, ctx: &'ctx Context) -> Self {
|
||||
let llvm_usize = generator.get_size_type(ctx);
|
||||
let llvm_list = Self::llvm_type(ctx, None, llvm_usize);
|
||||
|
||||
Self { ty: llvm_list, item: None, llvm_usize }
|
||||
}
|
||||
|
||||
/// Creates an [`ListType`] from a [unifier type][Type].
|
||||
#[must_use]
|
||||
pub fn from_unifier_type<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
ty: Type,
|
||||
) -> Self {
|
||||
// Check unifier type and extract `item_type`
|
||||
let elem_type = match &*ctx.unifier.get_ty_immutable(ty) {
|
||||
TypeEnum::TObj { obj_id, params, .. }
|
||||
if *obj_id == ctx.primitives.list.obj_id(&ctx.unifier).unwrap() =>
|
||||
{
|
||||
iter_type_vars(params).next().unwrap().ty
|
||||
}
|
||||
|
||||
_ => panic!("Expected `list` type, but got {}", ctx.unifier.stringify(ty)),
|
||||
};
|
||||
|
||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
||||
let llvm_elem_type = if let TypeEnum::TVar { .. } = &*ctx.unifier.get_ty_immutable(ty) {
|
||||
None
|
||||
} else {
|
||||
Some(ctx.get_llvm_type(generator, elem_type))
|
||||
};
|
||||
|
||||
Self {
|
||||
ty: Self::llvm_type(ctx.ctx, llvm_elem_type, llvm_usize),
|
||||
item: llvm_elem_type,
|
||||
llvm_usize,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an [`ListType`] from a [`PointerType`].
|
||||
@ -86,30 +171,39 @@ impl<'ctx> ListType<'ctx> {
|
||||
pub fn from_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
debug_assert!(Self::is_representable(ptr_ty, llvm_usize).is_ok());
|
||||
|
||||
ListType { ty: ptr_ty, llvm_usize }
|
||||
let ctx = ptr_ty.get_context();
|
||||
|
||||
// We are just searching for the index off a field - Slot an arbitrary element type in.
|
||||
let item_field_idx =
|
||||
Self::fields(ctx.i8_type().into(), llvm_usize).index_of_field(|f| f.items);
|
||||
let item = unsafe {
|
||||
ptr_ty
|
||||
.get_element_type()
|
||||
.into_struct_type()
|
||||
.get_field_type_at_index_unchecked(item_field_idx)
|
||||
.into_pointer_type()
|
||||
.get_element_type()
|
||||
};
|
||||
let item = BasicTypeEnum::try_from(item).unwrap_or_else(|()| {
|
||||
panic!(
|
||||
"Expected BasicTypeEnum for list element type, got {}",
|
||||
ptr_ty.get_element_type().print_to_string()
|
||||
)
|
||||
});
|
||||
|
||||
ListType { ty: ptr_ty, item: Some(item), llvm_usize }
|
||||
}
|
||||
|
||||
/// Returns the type of the `size` field of this `list` type.
|
||||
#[must_use]
|
||||
pub fn size_type(&self) -> IntType<'ctx> {
|
||||
self.as_base_type()
|
||||
.get_element_type()
|
||||
.into_struct_type()
|
||||
.get_field_type_at_index(1)
|
||||
.map(BasicTypeEnum::into_int_type)
|
||||
.unwrap()
|
||||
self.llvm_usize
|
||||
}
|
||||
|
||||
/// Returns the element type of this `list` type.
|
||||
#[must_use]
|
||||
pub fn element_type(&self) -> AnyTypeEnum<'ctx> {
|
||||
self.as_base_type()
|
||||
.get_element_type()
|
||||
.into_struct_type()
|
||||
.get_field_type_at_index(0)
|
||||
.map(BasicTypeEnum::into_pointer_type)
|
||||
.map(PointerType::get_element_type)
|
||||
.unwrap()
|
||||
pub fn element_type(&self) -> Option<BasicTypeEnum<'ctx>> {
|
||||
self.item
|
||||
}
|
||||
|
||||
/// Allocates an instance of [`ListValue`] as if by calling `alloca` on the base type.
|
||||
@ -127,6 +221,73 @@ impl<'ctx> ListType<'ctx> {
|
||||
)
|
||||
}
|
||||
|
||||
/// Allocates a [`ListValue`] on the stack using `item` of this [`ListType`] instance.
|
||||
///
|
||||
/// The returned list will contain:
|
||||
///
|
||||
/// - `data`: Allocated with `len` number of elements.
|
||||
/// - `len`: Initialized to the value of `len` passed to this function.
|
||||
#[must_use]
|
||||
pub fn construct<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
len: IntValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
let len = ctx.builder.build_int_z_extend(len, self.llvm_usize, "").unwrap();
|
||||
|
||||
// Generate a runtime assertion if allocating a non-empty list with unknown element type
|
||||
if ctx.registry.llvm_options.opt_level == OptimizationLevel::None && self.item.is_none() {
|
||||
let len_eqz = ctx
|
||||
.builder
|
||||
.build_int_compare(IntPredicate::EQ, len, self.llvm_usize.const_zero(), "")
|
||||
.unwrap();
|
||||
|
||||
ctx.make_assert(
|
||||
generator,
|
||||
len_eqz,
|
||||
"0:AssertionError",
|
||||
"Cannot allocate a non-empty list with unknown element type",
|
||||
[None, None, None],
|
||||
ctx.current_loc,
|
||||
);
|
||||
}
|
||||
|
||||
let plist = self.alloca(generator, ctx, name);
|
||||
plist.store_size(ctx, generator, len);
|
||||
|
||||
let item = self.item.unwrap_or(self.llvm_usize.into());
|
||||
plist.create_data(ctx, item, None);
|
||||
|
||||
plist
|
||||
}
|
||||
|
||||
/// Convenience function for creating a list with zero elements.
|
||||
///
|
||||
/// This function is preferred over [`ListType::construct`] if the length is known to always be
|
||||
/// 0, as this function avoids injecting an IR assertion for checking if a non-empty untyped
|
||||
/// list is being allocated.
|
||||
///
|
||||
/// The returned list will contain:
|
||||
///
|
||||
/// - `data`: Initialized to `(T*) 0`.
|
||||
/// - `len`: Initialized to `0`.
|
||||
#[must_use]
|
||||
pub fn construct_empty<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
let plist = self.alloca(generator, ctx, name);
|
||||
|
||||
plist.store_size(ctx, generator, self.llvm_usize.const_zero());
|
||||
plist.create_data(ctx, self.item.unwrap_or(self.llvm_usize.into()), None);
|
||||
|
||||
plist
|
||||
}
|
||||
|
||||
/// Converts an existing value into a [`ListValue`].
|
||||
#[must_use]
|
||||
pub fn map_value(
|
||||
@ -167,7 +328,7 @@ impl<'ctx> ProxyType<'ctx> for ListType<'ctx> {
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self::Value as ProxyValue<'ctx>>::Base {
|
||||
) -> PointerValue<'ctx> {
|
||||
generator
|
||||
.gen_var_alloc(
|
||||
ctx,
|
||||
|
@ -16,7 +16,11 @@
|
||||
//! the returned object. This is similar to a `new` expression in C++ but the object is allocated
|
||||
//! on the stack.
|
||||
|
||||
use inkwell::{context::Context, types::BasicType, values::IntValue};
|
||||
use inkwell::{
|
||||
context::Context,
|
||||
types::BasicType,
|
||||
values::{IntValue, PointerValue},
|
||||
};
|
||||
|
||||
use super::{
|
||||
values::{ArraySliceValue, ProxyValue},
|
||||
@ -24,11 +28,13 @@ use super::{
|
||||
};
|
||||
pub use list::*;
|
||||
pub use range::*;
|
||||
pub use tuple::*;
|
||||
|
||||
mod list;
|
||||
pub mod ndarray;
|
||||
mod range;
|
||||
pub mod structure;
|
||||
mod tuple;
|
||||
pub mod utils;
|
||||
|
||||
/// A LLVM type that is used to represent a corresponding type in NAC3.
|
||||
@ -53,13 +59,14 @@ pub trait ProxyType<'ctx>: Into<Self::Base> {
|
||||
llvm_ty: Self::Base,
|
||||
) -> Result<(), String>;
|
||||
|
||||
/// Creates a new value of this type, returning the LLVM instance of this value.
|
||||
/// Creates a new value of this type by invoking `alloca`, returning a [`PointerValue`] instance
|
||||
/// representing the allocated value.
|
||||
fn raw_alloca<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self::Value as ProxyValue<'ctx>>::Base;
|
||||
) -> PointerValue<'ctx>;
|
||||
|
||||
/// Creates a new array value of this type, returning an [`ArraySliceValue`] encapsulating the
|
||||
/// resulting array.
|
||||
|
@ -31,7 +31,7 @@ pub struct ContiguousNDArrayType<'ctx> {
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, StructFields)]
|
||||
pub struct ContiguousNDArrayFields<'ctx> {
|
||||
pub struct ContiguousNDArrayStructFields<'ctx> {
|
||||
#[value_type(usize)]
|
||||
pub ndims: StructField<'ctx, IntValue<'ctx>>,
|
||||
#[value_type(usize.ptr_type(AddressSpace::default()))]
|
||||
@ -40,12 +40,12 @@ pub struct ContiguousNDArrayFields<'ctx> {
|
||||
pub data: StructField<'ctx, PointerValue<'ctx>>,
|
||||
}
|
||||
|
||||
impl<'ctx> ContiguousNDArrayFields<'ctx> {
|
||||
impl<'ctx> ContiguousNDArrayStructFields<'ctx> {
|
||||
#[must_use]
|
||||
pub fn new_typed(item: BasicTypeEnum<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
let mut counter = FieldIndexCounter::default();
|
||||
|
||||
ContiguousNDArrayFields {
|
||||
ContiguousNDArrayStructFields {
|
||||
ndims: StructField::create(&mut counter, "ndims", llvm_usize),
|
||||
shape: StructField::create(
|
||||
&mut counter,
|
||||
@ -72,7 +72,7 @@ impl<'ctx> ContiguousNDArrayType<'ctx> {
|
||||
));
|
||||
};
|
||||
|
||||
let fields = ContiguousNDArrayFields::new(ctx, llvm_usize);
|
||||
let fields = ContiguousNDArrayStructFields::new(ctx, llvm_usize);
|
||||
|
||||
check_struct_type_matches_fields(
|
||||
fields,
|
||||
@ -93,14 +93,14 @@ impl<'ctx> ContiguousNDArrayType<'ctx> {
|
||||
fn fields(
|
||||
item: BasicTypeEnum<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> ContiguousNDArrayFields<'ctx> {
|
||||
ContiguousNDArrayFields::new_typed(item, llvm_usize)
|
||||
) -> ContiguousNDArrayStructFields<'ctx> {
|
||||
ContiguousNDArrayStructFields::new_typed(item, llvm_usize)
|
||||
}
|
||||
|
||||
/// See [`NDArrayType::fields`].
|
||||
// TODO: Move this into e.g. StructProxyType
|
||||
#[must_use]
|
||||
pub fn get_fields(&self) -> ContiguousNDArrayFields<'ctx> {
|
||||
pub fn get_fields(&self) -> ContiguousNDArrayStructFields<'ctx> {
|
||||
Self::fields(self.item, self.llvm_usize)
|
||||
}
|
||||
|
||||
@ -218,7 +218,7 @@ impl<'ctx> ProxyType<'ctx> for ContiguousNDArrayType<'ctx> {
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self::Value as ProxyValue<'ctx>>::Base {
|
||||
) -> PointerValue<'ctx> {
|
||||
generator
|
||||
.gen_var_alloc(
|
||||
ctx,
|
||||
|
139
nac3core/src/codegen/types/ndarray/factory.rs
Normal file
139
nac3core/src/codegen/types/ndarray/factory.rs
Normal file
@ -0,0 +1,139 @@
|
||||
use crate::codegen::{irrt, CodeGenContext, CodeGenerator};
|
||||
use crate::typecheck::typedef::Type;
|
||||
use inkwell::values::{BasicValueEnum, IntValue};
|
||||
use nac3core::codegen::values::ndarray::NDArrayValue;
|
||||
use crate::codegen::types::ndarray::NDArrayType;
|
||||
use crate::codegen::types::ProxyType;
|
||||
use crate::codegen::values::TypedArrayLikeAccessor;
|
||||
|
||||
/// Get the zero value in `np.zeros()` of a `dtype`.
|
||||
// TODO: Make this non-pub
|
||||
pub fn ndarray_zero_value<'ctx, G: CodeGenerator + ?Sized>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
dtype: Type,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
if [ctx.primitives.int32, ctx.primitives.uint32]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(dtype, *ty))
|
||||
{
|
||||
ctx.ctx.i32_type().const_zero().into()
|
||||
} else if [ctx.primitives.int64, ctx.primitives.uint64]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(dtype, *ty))
|
||||
{
|
||||
ctx.ctx.i64_type().const_zero().into()
|
||||
} else if ctx.unifier.unioned(dtype, ctx.primitives.float) {
|
||||
ctx.ctx.f64_type().const_zero().into()
|
||||
} else if ctx.unifier.unioned(dtype, ctx.primitives.bool) {
|
||||
ctx.ctx.bool_type().const_zero().into()
|
||||
} else if ctx.unifier.unioned(dtype, ctx.primitives.str) {
|
||||
ctx.gen_string(generator, "").into()
|
||||
} else {
|
||||
panic!("unrecognized dtype: {}", ctx.unifier.stringify(dtype));
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the one value in `np.ones()` of a `dtype`.
|
||||
// TODO: Make this non-pub
|
||||
pub fn ndarray_one_value<'ctx, G: CodeGenerator + ?Sized>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
dtype: Type,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
if [ctx.primitives.int32, ctx.primitives.uint32]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(dtype, *ty))
|
||||
{
|
||||
let is_signed = ctx.unifier.unioned(dtype, ctx.primitives.int32);
|
||||
ctx.ctx.i32_type().const_int(1, is_signed).into()
|
||||
} else if [ctx.primitives.int64, ctx.primitives.uint64]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(dtype, *ty))
|
||||
{
|
||||
let is_signed = ctx.unifier.unioned(dtype, ctx.primitives.int64);
|
||||
ctx.ctx.i64_type().const_int(1, is_signed).into()
|
||||
} else if ctx.unifier.unioned(dtype, ctx.primitives.float) {
|
||||
ctx.ctx.f64_type().const_float(1.0).into()
|
||||
} else if ctx.unifier.unioned(dtype, ctx.primitives.bool) {
|
||||
ctx.ctx.bool_type().const_int(1, false).into()
|
||||
} else if ctx.unifier.unioned(dtype, ctx.primitives.str) {
|
||||
ctx.gen_string(generator, "1").into()
|
||||
} else {
|
||||
panic!("unrecognized dtype: {}", ctx.unifier.stringify(dtype));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> NDArrayType<'ctx> {
|
||||
/// Create an ndarray like `np.empty`.
|
||||
pub fn construct_numpy_empty<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
shape: impl TypedArrayLikeAccessor<'ctx, IntValue<'ctx>>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
let ndarray = self.construct_uninitialized(generator, ctx, None);
|
||||
|
||||
// Validate `shape`
|
||||
let ndims_llvm = self.llvm_usize.const_int(self.ndims.unwrap(), false);
|
||||
irrt::ndarray::call_nac3_ndarray_util_assert_shape_no_negative(generator, ctx, ndims_llvm, shape.base_ptr(ctx, generator));
|
||||
|
||||
ndarray.copy_shape_from_array(generator, ctx, shape.base_ptr(ctx, generator));
|
||||
unsafe { ndarray.create_data(generator, ctx) };
|
||||
|
||||
ndarray
|
||||
}
|
||||
|
||||
/// Create an ndarray like `np.full`.
|
||||
pub fn construct_numpy_full<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
shape: impl TypedArrayLikeAccessor<'ctx, IntValue<'ctx>>,
|
||||
fill_value: BasicValueEnum<'ctx>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
let ndarray = self.construct_numpy_empty(generator, ctx, shape);
|
||||
ndarray.fill(generator, ctx, fill_value);
|
||||
ndarray
|
||||
}
|
||||
|
||||
/// Create an ndarray like `np.zero`.
|
||||
pub fn construct_numpy_zeros<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
dtype: Type,
|
||||
shape: impl TypedArrayLikeAccessor<'ctx, IntValue<'ctx>>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
assert_eq!(
|
||||
ctx.get_llvm_type(generator, dtype),
|
||||
self.dtype,
|
||||
"Expected LLVM dtype={} but got {}",
|
||||
self.dtype.print_to_string(),
|
||||
ctx.get_llvm_type(generator, dtype).print_to_string(),
|
||||
);
|
||||
|
||||
let fill_value = ndarray_zero_value(generator, ctx, dtype);
|
||||
self.construct_numpy_full(generator, ctx, shape, fill_value)
|
||||
}
|
||||
|
||||
/// Create an ndarray like `np.ones`.
|
||||
pub fn construct_numpy_ones<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
dtype: Type,
|
||||
shape: impl TypedArrayLikeAccessor<'ctx, IntValue<'ctx>>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
assert_eq!(
|
||||
ctx.get_llvm_type(generator, dtype),
|
||||
self.dtype,
|
||||
"Expected LLVM dtype={} but got {}",
|
||||
self.dtype.print_to_string(),
|
||||
ctx.get_llvm_type(generator, dtype).print_to_string(),
|
||||
);
|
||||
|
||||
let fill_value = ndarray_one_value(generator, ctx, dtype);
|
||||
self.construct_numpy_full(generator, ctx, shape, fill_value)
|
||||
}
|
||||
}
|
@ -176,7 +176,7 @@ impl<'ctx> ProxyType<'ctx> for NDIndexType<'ctx> {
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self::Value as ProxyValue<'ctx>>::Base {
|
||||
) -> PointerValue<'ctx> {
|
||||
generator
|
||||
.gen_var_alloc(
|
||||
ctx,
|
||||
|
@ -25,6 +25,7 @@ pub use indexing::*;
|
||||
pub use nditer::*;
|
||||
|
||||
mod contiguous;
|
||||
pub mod factory;
|
||||
mod indexing;
|
||||
mod nditer;
|
||||
|
||||
@ -430,7 +431,7 @@ impl<'ctx> ProxyType<'ctx> for NDArrayType<'ctx> {
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self::Value as ProxyValue<'ctx>>::Base {
|
||||
) -> PointerValue<'ctx> {
|
||||
generator
|
||||
.gen_var_alloc(
|
||||
ctx,
|
||||
|
@ -135,8 +135,10 @@ impl<'ctx> NDIterType<'ctx> {
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
ndarray: NDArrayValue<'ctx>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
assert!(ndarray.get_type().ndims().is_some(), "NDIter requires ndims of NDArray to be known.");
|
||||
|
||||
let nditer = self.raw_alloca(generator, ctx, None);
|
||||
let ndims = ndarray.load_ndims(ctx);
|
||||
let ndims = self.llvm_usize.const_int(ndarray.get_type().ndims().unwrap(), false);
|
||||
|
||||
// The caller has the responsibility to allocate 'indices' for `NDIter`.
|
||||
let indices =
|
||||
@ -202,7 +204,7 @@ impl<'ctx> ProxyType<'ctx> for NDIterType<'ctx> {
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self::Value as ProxyValue<'ctx>>::Base {
|
||||
) -> PointerValue<'ctx> {
|
||||
generator
|
||||
.gen_var_alloc(
|
||||
ctx,
|
||||
|
@ -1,7 +1,7 @@
|
||||
use inkwell::{
|
||||
context::Context,
|
||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
|
||||
values::IntValue,
|
||||
values::{IntValue, PointerValue},
|
||||
AddressSpace,
|
||||
};
|
||||
|
||||
@ -131,7 +131,7 @@ impl<'ctx> ProxyType<'ctx> for RangeType<'ctx> {
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self::Value as ProxyValue<'ctx>>::Base {
|
||||
) -> PointerValue<'ctx> {
|
||||
generator
|
||||
.gen_var_alloc(
|
||||
ctx,
|
||||
|
@ -5,6 +5,7 @@ use inkwell::{
|
||||
types::{BasicTypeEnum, IntType, StructType},
|
||||
values::{BasicValue, BasicValueEnum, IntValue, PointerValue, StructValue},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::codegen::CodeGenContext;
|
||||
|
||||
@ -55,6 +56,20 @@ pub trait StructFields<'ctx>: Eq + Copy {
|
||||
{
|
||||
self.into_vec().into_iter()
|
||||
}
|
||||
|
||||
/// Returns the field index of a field in this structure.
|
||||
fn index_of_field<V>(&self, name: impl FnOnce(&Self) -> StructField<'ctx, V>) -> u32
|
||||
where
|
||||
V: BasicValue<'ctx> + TryFrom<BasicValueEnum<'ctx>, Error = ()>,
|
||||
{
|
||||
let field_name = name(self).name;
|
||||
self.index_of_field_name(field_name).unwrap()
|
||||
}
|
||||
|
||||
/// Returns the field index of a field with the given name in this structure.
|
||||
fn index_of_field_name(&self, field_name: &str) -> Option<u32> {
|
||||
self.iter().find_position(|(name, _)| *name == field_name).map(|(idx, _)| idx as u32)
|
||||
}
|
||||
}
|
||||
|
||||
/// A single field of an LLVM structure.
|
||||
|
193
nac3core/src/codegen/types/tuple.rs
Normal file
193
nac3core/src/codegen/types/tuple.rs
Normal file
@ -0,0 +1,193 @@
|
||||
use inkwell::{
|
||||
context::Context,
|
||||
types::{BasicType, BasicTypeEnum, IntType, StructType},
|
||||
values::{BasicValueEnum, IntValue, PointerValue},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
use super::ProxyType;
|
||||
use crate::{
|
||||
codegen::{
|
||||
values::{ArraySliceValue, ProxyValue, TupleValue},
|
||||
CodeGenContext, CodeGenerator,
|
||||
},
|
||||
typecheck::typedef::{Type, TypeEnum},
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct TupleType<'ctx> {
|
||||
ty: StructType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
}
|
||||
|
||||
impl<'ctx> TupleType<'ctx> {
|
||||
/// Checks whether `llvm_ty` represents any tuple type, returning [Err] if it does not.
|
||||
pub fn is_representable(_value: StructType<'ctx>) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Creates an LLVM type corresponding to the expected structure of a tuple.
|
||||
#[must_use]
|
||||
fn llvm_type(ctx: &'ctx Context, tys: &[BasicTypeEnum<'ctx>]) -> StructType<'ctx> {
|
||||
ctx.struct_type(tys, false)
|
||||
}
|
||||
|
||||
/// Creates an instance of [`TupleType`].
|
||||
#[must_use]
|
||||
pub fn new<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
tys: &[BasicTypeEnum<'ctx>],
|
||||
) -> Self {
|
||||
let llvm_usize = generator.get_size_type(ctx);
|
||||
let llvm_tuple = Self::llvm_type(ctx, tys);
|
||||
|
||||
Self { ty: llvm_tuple, llvm_usize }
|
||||
}
|
||||
|
||||
/// Creates an [`TupleType`] from a [unifier type][Type].
|
||||
#[must_use]
|
||||
pub fn from_unifier_type<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
ty: Type,
|
||||
) -> Self {
|
||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
||||
|
||||
// Sanity check on object type.
|
||||
let TypeEnum::TTuple { ty: tys, .. } = &*ctx.unifier.get_ty_immutable(ty) else {
|
||||
panic!("Expected type to be a TypeEnum::TTuple, got {}", ctx.unifier.stringify(ty));
|
||||
};
|
||||
|
||||
let llvm_tys = tys.iter().map(|ty| ctx.get_llvm_type(generator, *ty)).collect_vec();
|
||||
Self { ty: Self::llvm_type(ctx.ctx, &llvm_tys), llvm_usize }
|
||||
}
|
||||
|
||||
/// Creates an [`TupleType`] from a [`StructType`].
|
||||
#[must_use]
|
||||
pub fn from_type(struct_ty: StructType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
debug_assert!(Self::is_representable(struct_ty).is_ok());
|
||||
|
||||
TupleType { ty: struct_ty, llvm_usize }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn num_elements(&self) -> u32 {
|
||||
self.ty.count_fields()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn type_at_index(&self, index: u32) -> Option<BasicTypeEnum<'ctx>> {
|
||||
if index < self.num_elements() {
|
||||
Some(unsafe { self.type_at_index_unchecked(index) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the type of the tuple element at the given `index`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that the index is valid.
|
||||
#[must_use]
|
||||
pub unsafe fn type_at_index_unchecked(&self, index: u32) -> BasicTypeEnum<'ctx> {
|
||||
self.ty.get_field_type_at_index_unchecked(index)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn construct(
|
||||
&self,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
self.map_value(Self::llvm_type(ctx.ctx, &self.ty.get_field_types()).const_zero(), name)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn construct_from_objects<I: IntoIterator<Item = BasicValueEnum<'ctx>>>(
|
||||
&self,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
objects: I,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
let values = objects.into_iter().collect_vec();
|
||||
|
||||
assert_eq!(values.len(), self.num_elements() as usize);
|
||||
assert!(values
|
||||
.iter()
|
||||
.enumerate()
|
||||
.all(|(i, v)| { v.get_type() == unsafe { self.type_at_index_unchecked(i as u32) } }));
|
||||
|
||||
let mut value = self.construct(ctx, name);
|
||||
for (i, val) in values.into_iter().enumerate() {
|
||||
value.store_element(ctx, i as u32, val);
|
||||
}
|
||||
|
||||
value
|
||||
}
|
||||
|
||||
/// Converts an existing value into a [`ListValue`].
|
||||
#[must_use]
|
||||
pub fn map_value(
|
||||
&self,
|
||||
value: <<Self as ProxyType<'ctx>>::Value as ProxyValue<'ctx>>::Base,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_struct_value(value, self.llvm_usize, name)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyType<'ctx> for TupleType<'ctx> {
|
||||
type Base = StructType<'ctx>;
|
||||
type Value = TupleValue<'ctx>;
|
||||
|
||||
fn is_type<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
llvm_ty: impl BasicType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
if let BasicTypeEnum::StructType(ty) = llvm_ty.as_basic_type_enum() {
|
||||
<Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
|
||||
} else {
|
||||
Err(format!("Expected struct type, got {llvm_ty:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_representable<G: CodeGenerator + ?Sized>(
|
||||
_generator: &G,
|
||||
_ctx: &'ctx Context,
|
||||
llvm_ty: Self::Base,
|
||||
) -> Result<(), String> {
|
||||
Self::is_representable(llvm_ty)
|
||||
}
|
||||
|
||||
fn raw_alloca<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> PointerValue<'ctx> {
|
||||
generator.gen_var_alloc(ctx, self.as_base_type().into(), name).unwrap()
|
||||
}
|
||||
|
||||
fn array_alloca<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_base_type().into(), size, name).unwrap()
|
||||
}
|
||||
|
||||
fn as_base_type(&self) -> Self::Base {
|
||||
self.ty
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> From<TupleType<'ctx>> for StructType<'ctx> {
|
||||
fn from(value: TupleType<'ctx>) -> Self {
|
||||
value.as_base_type()
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
use inkwell::{
|
||||
context::{AsContextRef, Context, ContextRef},
|
||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
|
||||
values::IntValue,
|
||||
values::{IntValue, PointerValue},
|
||||
AddressSpace,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
@ -215,7 +215,7 @@ impl<'ctx> ProxyType<'ctx> for SliceType<'ctx> {
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self::Value as ProxyValue<'ctx>>::Base {
|
||||
) -> PointerValue<'ctx> {
|
||||
generator
|
||||
.gen_var_alloc(
|
||||
ctx,
|
||||
|
@ -8,7 +8,7 @@ use super::{
|
||||
ArrayLikeIndexer, ArrayLikeValue, ProxyValue, UntypedArrayLikeAccessor, UntypedArrayLikeMutator,
|
||||
};
|
||||
use crate::codegen::{
|
||||
types::ListType,
|
||||
types::{structure::StructField, ListType},
|
||||
{CodeGenContext, CodeGenerator},
|
||||
};
|
||||
|
||||
@ -42,48 +42,26 @@ impl<'ctx> ListValue<'ctx> {
|
||||
ListValue { value: ptr, llvm_usize, name }
|
||||
}
|
||||
|
||||
fn items_field(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, PointerValue<'ctx>> {
|
||||
self.get_type().get_fields(&ctx.ctx).items
|
||||
}
|
||||
|
||||
/// Returns the double-indirection pointer to the `data` array, as if by calling `getelementptr`
|
||||
/// on the field.
|
||||
fn pptr_to_data(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
|
||||
let llvm_i32 = ctx.ctx.i32_type();
|
||||
let var_name = self.name.map(|v| format!("{v}.data.addr")).unwrap_or_default();
|
||||
|
||||
unsafe {
|
||||
ctx.builder
|
||||
.build_in_bounds_gep(
|
||||
self.as_base_value(),
|
||||
&[llvm_i32.const_zero(), llvm_i32.const_zero()],
|
||||
var_name.as_str(),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the pointer to the field storing the size of this `list`.
|
||||
fn ptr_to_size(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
|
||||
let llvm_i32 = ctx.ctx.i32_type();
|
||||
let var_name = self.name.map(|v| format!("{v}.size.addr")).unwrap_or_default();
|
||||
|
||||
unsafe {
|
||||
ctx.builder
|
||||
.build_in_bounds_gep(
|
||||
self.as_base_value(),
|
||||
&[llvm_i32.const_zero(), llvm_i32.const_int(1, true)],
|
||||
var_name.as_str(),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
self.items_field(ctx).ptr_by_gep(ctx, self.value, self.name)
|
||||
}
|
||||
|
||||
/// Stores the array of data elements `data` into this instance.
|
||||
fn store_data(&self, ctx: &CodeGenContext<'ctx, '_>, data: PointerValue<'ctx>) {
|
||||
ctx.builder.build_store(self.pptr_to_data(ctx), data).unwrap();
|
||||
self.items_field(ctx).set(ctx, self.value, data, self.name);
|
||||
}
|
||||
|
||||
/// Convenience method for creating a new array storing data elements with the given element
|
||||
/// type `elem_ty` and `size`.
|
||||
///
|
||||
/// If `size` is [None], the size stored in the field of this instance is used instead.
|
||||
/// If `size` is [None], the size stored in the field of this instance is used instead. If
|
||||
/// `size` is resolved to `0` at runtime, `(T*) 0` will be assigned to `data`.
|
||||
pub fn create_data(
|
||||
&self,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
@ -114,6 +92,15 @@ impl<'ctx> ListValue<'ctx> {
|
||||
ListDataProxy(self)
|
||||
}
|
||||
|
||||
fn len_field(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, IntValue<'ctx>> {
|
||||
self.get_type().get_fields(&ctx.ctx).len
|
||||
}
|
||||
|
||||
/// Returns the pointer to the field storing the size of this `list`.
|
||||
fn ptr_to_size(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
|
||||
self.len_field(ctx).ptr_by_gep(ctx, self.value, self.name)
|
||||
}
|
||||
|
||||
/// Stores the `size` of this `list` into this instance.
|
||||
pub fn store_size<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
@ -123,22 +110,16 @@ impl<'ctx> ListValue<'ctx> {
|
||||
) {
|
||||
debug_assert_eq!(size.get_type(), generator.get_size_type(ctx.ctx));
|
||||
|
||||
let psize = self.ptr_to_size(ctx);
|
||||
ctx.builder.build_store(psize, size).unwrap();
|
||||
self.len_field(ctx).set(ctx, self.value, size, self.name);
|
||||
}
|
||||
|
||||
/// Returns the size of this `list` as a value.
|
||||
pub fn load_size(&self, ctx: &CodeGenContext<'ctx, '_>, name: Option<&str>) -> IntValue<'ctx> {
|
||||
let psize = self.ptr_to_size(ctx);
|
||||
let var_name = name
|
||||
.map(ToString::to_string)
|
||||
.or_else(|| self.name.map(|v| format!("{v}.size")))
|
||||
.unwrap_or_default();
|
||||
|
||||
ctx.builder
|
||||
.build_load(psize, var_name.as_str())
|
||||
.map(BasicValueEnum::into_int_value)
|
||||
.unwrap()
|
||||
pub fn load_size(
|
||||
&self,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> IntValue<'ctx> {
|
||||
self.len_field(ctx).get(ctx, self.value, name)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,11 +5,13 @@ use crate::codegen::CodeGenerator;
|
||||
pub use array::*;
|
||||
pub use list::*;
|
||||
pub use range::*;
|
||||
pub use tuple::*;
|
||||
|
||||
mod array;
|
||||
mod list;
|
||||
pub mod ndarray;
|
||||
mod range;
|
||||
mod tuple;
|
||||
pub mod utils;
|
||||
|
||||
/// A LLVM type that is used to represent a non-primitive value in NAC3.
|
||||
|
@ -24,6 +24,7 @@ pub use view::*;
|
||||
mod contiguous;
|
||||
mod indexing;
|
||||
mod nditer;
|
||||
pub mod shape;
|
||||
mod view;
|
||||
|
||||
/// Proxy type for accessing an `NDArray` value in LLVM.
|
||||
@ -406,6 +407,23 @@ impl<'ctx> NDArrayValue<'ctx> {
|
||||
irrt::ndarray::call_nac3_ndarray_copy_data(generator, ctx, src, *self);
|
||||
}
|
||||
|
||||
/// Fill the ndarray with a scalar.
|
||||
///
|
||||
/// `fill_value` must have the same LLVM type as the `dtype` of this ndarray.
|
||||
pub fn fill<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
value: BasicValueEnum<'ctx>,
|
||||
) {
|
||||
self.foreach(generator, ctx, |_, ctx, _hooks, nditer| {
|
||||
let p = nditer.get_pointer(ctx);
|
||||
ctx.builder.build_store(p, value).unwrap();
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Returns true if this ndarray is unsized - `ndims == 0` and only contains a scalar.
|
||||
#[must_use]
|
||||
pub fn is_unsized(&self) -> Option<bool> {
|
||||
|
143
nac3core/src/codegen/values/ndarray/shape.rs
Normal file
143
nac3core/src/codegen/values/ndarray/shape.rs
Normal file
@ -0,0 +1,143 @@
|
||||
use crate::codegen::stmt::gen_for_callback_incrementing;
|
||||
use crate::codegen::types::{ListType, TupleType};
|
||||
use crate::codegen::values::{ArraySliceValue, ProxyValue, TypedArrayLikeAccessor, TypedArrayLikeAdapter, TypedArrayLikeMutator, UntypedArrayLikeAccessor};
|
||||
use crate::codegen::{CodeGenContext, CodeGenerator};
|
||||
use crate::typecheck::typedef::{Type, TypeEnum};
|
||||
use inkwell::values::{BasicValue, BasicValueEnum, IntValue};
|
||||
|
||||
/// Parse a NumPy-like "int sequence" input and return the int sequence as an array and its length.
|
||||
///
|
||||
/// * `sequence` - The `sequence` parameter.
|
||||
/// * `sequence_ty` - The typechecker type of `sequence`
|
||||
///
|
||||
/// The `sequence` argument type may only be one of the following:
|
||||
/// 1. A list of `int32`; e.g., `np.empty([600, 800, 3])`
|
||||
/// 2. A tuple of `int32`; e.g., `np.empty((600, 800, 3))`
|
||||
/// 3. A scalar `int32`; e.g., `np.empty(3)`, this is functionally equivalent to `np.empty([3])`
|
||||
///
|
||||
/// All `int32` values will be sign-extended to `SizeT`.
|
||||
pub fn parse_numpy_int_sequence<'ctx, G: CodeGenerator + ?Sized>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
(input_seq_ty, input_seq): (Type, BasicValueEnum<'ctx>),
|
||||
) -> impl TypedArrayLikeAccessor<'ctx, IntValue<'ctx>> {
|
||||
let llvm_usize = generator.get_size_type(ctx.ctx);
|
||||
let zero = llvm_usize.const_zero();
|
||||
let one = llvm_usize.const_int(1, false);
|
||||
|
||||
// The result `list` to return.
|
||||
match &*ctx.unifier.get_ty_immutable(input_seq_ty) {
|
||||
TypeEnum::TObj { obj_id, .. }
|
||||
if *obj_id == ctx.primitives.list.obj_id(&ctx.unifier).unwrap() =>
|
||||
{
|
||||
// 1. A list of `int32`; e.g., `np.empty([600, 800, 3])`
|
||||
|
||||
let input_seq = ListType::from_unifier_type(generator, ctx, input_seq_ty)
|
||||
.map_value(input_seq.into_pointer_value(), None);
|
||||
|
||||
let len = input_seq.load_size(ctx, None);
|
||||
// TODO: Find a way to remove this mid-BB allocation
|
||||
let result = ctx.builder.build_array_alloca(llvm_usize, len, "").unwrap();
|
||||
let result = TypedArrayLikeAdapter::from(
|
||||
ArraySliceValue::from_ptr_val(result, len, None),
|
||||
Box::new(|_, val| val.into_int_value()),
|
||||
Box::new(|_, val| val.as_basic_value_enum()),
|
||||
);
|
||||
|
||||
// Load all the `int32`s from the input_sequence, cast them to `SizeT`, and store them into `result`
|
||||
gen_for_callback_incrementing(
|
||||
generator,
|
||||
ctx,
|
||||
None,
|
||||
zero,
|
||||
(len, false),
|
||||
|generator, ctx, _, i| {
|
||||
// Load the i-th int32 in the input sequence
|
||||
let int = unsafe {
|
||||
input_seq.data().get_unchecked(ctx, generator, &i, None).into_int_value()
|
||||
};
|
||||
|
||||
// Cast to SizeT
|
||||
let int =
|
||||
ctx.builder.build_int_s_extend_or_bit_cast(int, llvm_usize, "").unwrap();
|
||||
|
||||
// Store
|
||||
unsafe { result.set_typed_unchecked(ctx, generator, &i, int) };
|
||||
|
||||
Ok(())
|
||||
},
|
||||
one,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
TypeEnum::TTuple { .. } => {
|
||||
// 2. A tuple of ints; e.g., `np.empty((600, 800, 3))`
|
||||
|
||||
let input_seq = TupleType::from_unifier_type(generator, ctx, input_seq_ty)
|
||||
.map_value(input_seq.into_struct_value(), None);
|
||||
|
||||
let len = input_seq.get_type().num_elements();
|
||||
|
||||
let result = generator
|
||||
.gen_array_var_alloc(
|
||||
ctx,
|
||||
llvm_usize.into(),
|
||||
llvm_usize.const_int(u64::from(len), false),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
let result = TypedArrayLikeAdapter::from(
|
||||
result,
|
||||
Box::new(|_, val| val.into_int_value()),
|
||||
Box::new(|_, val| val.as_basic_value_enum()),
|
||||
);
|
||||
|
||||
for i in 0..input_seq.get_type().num_elements() {
|
||||
// Get the i-th element off of the tuple and load it into `result`.
|
||||
let int = input_seq.load_element(ctx, i).into_int_value();
|
||||
let int = ctx.builder.build_int_s_extend_or_bit_cast(int, llvm_usize, "").unwrap();
|
||||
|
||||
unsafe {
|
||||
result.set_typed_unchecked(
|
||||
ctx,
|
||||
generator,
|
||||
&llvm_usize.const_int(u64::from(i), false),
|
||||
int,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
TypeEnum::TObj { obj_id, .. }
|
||||
if *obj_id == ctx.primitives.int32.obj_id(&ctx.unifier).unwrap() =>
|
||||
{
|
||||
// 3. A scalar int; e.g., `np.empty(3)`, this is functionally equivalent to `np.empty([3])`
|
||||
|
||||
let input_int = input_seq.into_int_value();
|
||||
|
||||
let len = one;
|
||||
let result = generator.gen_array_var_alloc(ctx, llvm_usize.into(), len, None).unwrap();
|
||||
let result = TypedArrayLikeAdapter::from(
|
||||
result,
|
||||
Box::new(|_, val| val.into_int_value()),
|
||||
Box::new(|_, val| val.as_basic_value_enum()),
|
||||
);
|
||||
let int =
|
||||
ctx.builder.build_int_s_extend_or_bit_cast(input_int, llvm_usize, "").unwrap();
|
||||
|
||||
// Storing into result[0]
|
||||
unsafe {
|
||||
result.set_typed_unchecked(ctx, generator, &zero, int);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
_ => panic!("encountered unknown sequence type: {}", ctx.unifier.stringify(input_seq_ty)),
|
||||
}
|
||||
}
|
76
nac3core/src/codegen/values/tuple.rs
Normal file
76
nac3core/src/codegen/values/tuple.rs
Normal file
@ -0,0 +1,76 @@
|
||||
use inkwell::{
|
||||
types::IntType,
|
||||
values::{BasicValue, BasicValueEnum, StructValue},
|
||||
};
|
||||
|
||||
use super::ProxyValue;
|
||||
use crate::codegen::{types::TupleType, CodeGenContext};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct TupleValue<'ctx> {
|
||||
value: StructValue<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
}
|
||||
|
||||
impl<'ctx> TupleValue<'ctx> {
|
||||
/// Checks whether `value` is an instance of `tuple`, returning [Err] if `value` is not an
|
||||
/// instance.
|
||||
pub fn is_representable(
|
||||
value: StructValue<'ctx>,
|
||||
_llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
TupleType::is_representable(value.get_type())
|
||||
}
|
||||
|
||||
/// Creates an [`TupleValue`] from a [`StructValue`].
|
||||
#[must_use]
|
||||
pub fn from_struct_value(
|
||||
value: StructValue<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
debug_assert!(Self::is_representable(value, llvm_usize).is_ok());
|
||||
|
||||
Self { value, llvm_usize, name }
|
||||
}
|
||||
|
||||
/// Stores a value into the tuple element at the given `index`.
|
||||
pub fn store_element(
|
||||
&mut self,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
index: u32,
|
||||
element: impl BasicValue<'ctx>,
|
||||
) {
|
||||
assert_eq!(element.as_basic_value_enum().get_type(), unsafe {
|
||||
self.get_type().type_at_index_unchecked(index)
|
||||
});
|
||||
|
||||
let new_value = ctx.builder.build_insert_value(self.value, element, index, "").unwrap();
|
||||
self.value = new_value.into_struct_value();
|
||||
}
|
||||
|
||||
/// Loads a value from the tuple element at the given `index`.
|
||||
pub fn load_element(&self, ctx: &CodeGenContext<'ctx, '_>, index: u32) -> BasicValueEnum<'ctx> {
|
||||
ctx.builder.build_extract_value(self.value, index, "tuple[{i}]").unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyValue<'ctx> for TupleValue<'ctx> {
|
||||
type Base = StructValue<'ctx>;
|
||||
type Type = TupleType<'ctx>;
|
||||
|
||||
fn get_type(&self) -> Self::Type {
|
||||
TupleType::from_type(self.as_base_value().get_type(), self.llvm_usize)
|
||||
}
|
||||
|
||||
fn as_base_value(&self) -> Self::Base {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> From<TupleValue<'ctx>> for StructValue<'ctx> {
|
||||
fn from(value: TupleValue<'ctx>) -> Self {
|
||||
value.as_base_value()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user