forked from M-Labs/nac3
core: refactor range with model and RangeObject
This commit is contained in:
parent
b13b28cfe8
commit
6528115d6a
|
@ -1,10 +1,10 @@
|
||||||
use nac3core::{
|
use nac3core::{
|
||||||
codegen::{
|
codegen::{
|
||||||
classes::{ListValue, RangeValue, UntypedArrayLikeAccessor},
|
classes::{ListValue, UntypedArrayLikeAccessor},
|
||||||
expr::{destructure_range, gen_call},
|
expr::gen_call,
|
||||||
llvm_intrinsics::{call_int_smax, call_stackrestore, call_stacksave},
|
llvm_intrinsics::{call_int_smax, call_stackrestore, call_stacksave},
|
||||||
model::*,
|
model::*,
|
||||||
object::{any::AnyObject, ndarray::NDArrayObject},
|
object::{any::AnyObject, ndarray::NDArrayObject, range::RangeObject},
|
||||||
stmt::{gen_block, gen_for_callback_incrementing, gen_if_callback, gen_with},
|
stmt::{gen_block, gen_for_callback_incrementing, gen_if_callback, gen_with},
|
||||||
CodeGenContext, CodeGenerator,
|
CodeGenContext, CodeGenerator,
|
||||||
},
|
},
|
||||||
|
@ -1121,9 +1121,13 @@ fn polymorphic_print<'ctx>(
|
||||||
fmt.push_str("range(");
|
fmt.push_str("range(");
|
||||||
flush(ctx, generator, &mut fmt, &mut args);
|
flush(ctx, generator, &mut fmt, &mut args);
|
||||||
|
|
||||||
let val = RangeValue::from_ptr_val(value.into_pointer_value(), None);
|
let range = AnyObject { ty, value };
|
||||||
|
let range = RangeObject::from_object(generator, ctx, range);
|
||||||
|
|
||||||
let (start, stop, step) = destructure_range(ctx, val);
|
let (start, stop, step) = range.instance.destructure(generator, ctx);
|
||||||
|
let start = start.value;
|
||||||
|
let stop = stop.value;
|
||||||
|
let step = step.value;
|
||||||
|
|
||||||
polymorphic_print(
|
polymorphic_print(
|
||||||
ctx,
|
ctx,
|
||||||
|
|
|
@ -39,3 +39,11 @@ template <typename T> struct Range
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
int32_t __nac3_range_len_i32(int32_t start, int32_t stop, int32_t step)
|
||||||
|
{
|
||||||
|
return range::len(start, stop, step);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,19 +3,17 @@ use inkwell::values::{BasicValue, BasicValueEnum, IntValue};
|
||||||
use inkwell::{FloatPredicate, IntPredicate, OptimizationLevel};
|
use inkwell::{FloatPredicate, IntPredicate, OptimizationLevel};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
use crate::codegen::object::ndarray::{NDArrayOut, ScalarOrNDArray};
|
||||||
|
use crate::codegen::object::range::RangeObject;
|
||||||
|
use crate::codegen::{extern_fns, irrt, llvm_intrinsics, CodeGenContext, CodeGenerator};
|
||||||
|
use crate::toplevel::helper::PrimDef;
|
||||||
|
use crate::typecheck::typedef::{Type, TypeEnum};
|
||||||
|
|
||||||
use super::model::*;
|
use super::model::*;
|
||||||
use super::object::any::AnyObject;
|
use super::object::any::AnyObject;
|
||||||
use super::object::list::ListObject;
|
use super::object::list::ListObject;
|
||||||
use super::object::ndarray::NDArrayObject;
|
use super::object::ndarray::NDArrayObject;
|
||||||
use super::object::tuple::TupleObject;
|
use super::object::tuple::TupleObject;
|
||||||
use crate::codegen::classes::RangeValue;
|
|
||||||
use crate::codegen::expr::destructure_range;
|
|
||||||
use crate::codegen::irrt::calculate_len_for_slice_range;
|
|
||||||
use crate::codegen::object::ndarray::{NDArrayOut, ScalarOrNDArray};
|
|
||||||
use crate::codegen::{extern_fns, irrt, llvm_intrinsics, CodeGenContext, CodeGenerator};
|
|
||||||
use crate::toplevel::helper::PrimDef;
|
|
||||||
use crate::typecheck::typedef::Type;
|
|
||||||
use crate::typecheck::typedef::TypeEnum;
|
|
||||||
|
|
||||||
/// Shorthand for [`unreachable!()`] when a type of argument is not supported.
|
/// Shorthand for [`unreachable!()`] when a type of argument is not supported.
|
||||||
///
|
///
|
||||||
|
@ -31,20 +29,20 @@ fn unsupported_type(ctx: &CodeGenContext<'_, '_>, fn_name: &str, tys: &[Type]) -
|
||||||
pub fn call_len<'ctx, G: CodeGenerator + ?Sized>(
|
pub fn call_len<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
n: (Type, BasicValueEnum<'ctx>),
|
(arg_ty, arg): (Type, BasicValueEnum<'ctx>),
|
||||||
) -> Result<IntValue<'ctx>, String> {
|
) -> Result<IntValue<'ctx>, String> {
|
||||||
let (arg_ty, arg) = n;
|
|
||||||
Ok(if ctx.unifier.unioned(arg_ty, ctx.primitives.range) {
|
|
||||||
let arg = RangeValue::from_ptr_val(arg.into_pointer_value(), Some("range"));
|
|
||||||
let (start, end, step) = destructure_range(ctx, arg);
|
|
||||||
calculate_len_for_slice_range(generator, ctx, start, end, step)
|
|
||||||
} else {
|
|
||||||
let arg = AnyObject { ty: arg_ty, value: arg };
|
let arg = AnyObject { ty: arg_ty, value: arg };
|
||||||
let len: Instance<'ctx, Int<Int32>> = match &*ctx.unifier.get_ty(arg_ty) {
|
let len: Instance<'ctx, Int<Int32>> = match &*ctx.unifier.get_ty(arg_ty) {
|
||||||
TypeEnum::TTuple { .. } => {
|
TypeEnum::TTuple { .. } => {
|
||||||
let tuple = TupleObject::from_object(ctx, arg);
|
let tuple = TupleObject::from_object(ctx, arg);
|
||||||
tuple.len(generator, ctx).truncate_or_bit_cast(generator, ctx, Int32)
|
tuple.len(generator, ctx).truncate_or_bit_cast(generator, ctx, Int32)
|
||||||
}
|
}
|
||||||
|
TypeEnum::TObj { obj_id, .. }
|
||||||
|
if *obj_id == ctx.primitives.range.obj_id(&ctx.unifier).unwrap() =>
|
||||||
|
{
|
||||||
|
let range = RangeObject::from_object(generator, ctx, arg);
|
||||||
|
range.len(generator, ctx)
|
||||||
|
}
|
||||||
TypeEnum::TObj { obj_id, .. }
|
TypeEnum::TObj { obj_id, .. }
|
||||||
if *obj_id == ctx.primitives.ndarray.obj_id(&ctx.unifier).unwrap() =>
|
if *obj_id == ctx.primitives.ndarray.obj_id(&ctx.unifier).unwrap() =>
|
||||||
{
|
{
|
||||||
|
@ -59,8 +57,7 @@ pub fn call_len<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
}
|
}
|
||||||
_ => unsupported_type(ctx, "len", &[arg_ty]),
|
_ => unsupported_type(ctx, "len", &[arg_ty]),
|
||||||
};
|
};
|
||||||
len.value
|
Ok(len.value)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Invokes the `int32` builtin function.
|
/// Invokes the `int32` builtin function.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::codegen::{CodeGenContext, CodeGenerator};
|
use crate::codegen::{CodeGenContext, CodeGenerator};
|
||||||
use inkwell::context::Context;
|
use inkwell::context::Context;
|
||||||
use inkwell::types::{ArrayType, BasicType, StructType};
|
use inkwell::types::{BasicType, StructType};
|
||||||
use inkwell::values::{ArrayValue, BasicValue, StructValue};
|
use inkwell::values::{BasicValue, StructValue};
|
||||||
use inkwell::{
|
use inkwell::{
|
||||||
types::{AnyTypeEnum, BasicTypeEnum, IntType, PointerType},
|
types::{AnyTypeEnum, BasicTypeEnum, IntType, PointerType},
|
||||||
values::{BasicValueEnum, IntValue, PointerValue},
|
values::{BasicValueEnum, IntValue, PointerValue},
|
||||||
|
@ -873,266 +873,3 @@ impl<'ctx> ArrayLikeIndexer<'ctx> for ListDataProxy<'ctx, '_> {
|
||||||
|
|
||||||
impl<'ctx> UntypedArrayLikeAccessor<'ctx> for ListDataProxy<'ctx, '_> {}
|
impl<'ctx> UntypedArrayLikeAccessor<'ctx> for ListDataProxy<'ctx, '_> {}
|
||||||
impl<'ctx> UntypedArrayLikeMutator<'ctx> for ListDataProxy<'ctx, '_> {}
|
impl<'ctx> UntypedArrayLikeMutator<'ctx> for ListDataProxy<'ctx, '_> {}
|
||||||
|
|
||||||
/// Proxy type for a `range` type in LLVM.
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
||||||
pub struct RangeType<'ctx> {
|
|
||||||
ty: PointerType<'ctx>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> RangeType<'ctx> {
|
|
||||||
/// Checks whether `llvm_ty` represents a `range` type, returning [Err] if it does not.
|
|
||||||
pub fn is_type(llvm_ty: PointerType<'ctx>) -> Result<(), String> {
|
|
||||||
let llvm_range_ty = llvm_ty.get_element_type();
|
|
||||||
let AnyTypeEnum::ArrayType(llvm_range_ty) = llvm_range_ty else {
|
|
||||||
return Err(format!("Expected array type for `range` type, got {llvm_range_ty}"));
|
|
||||||
};
|
|
||||||
if llvm_range_ty.len() != 3 {
|
|
||||||
return Err(format!(
|
|
||||||
"Expected 3 elements for `range` type, got {}",
|
|
||||||
llvm_range_ty.len()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let llvm_range_elem_ty = llvm_range_ty.get_element_type();
|
|
||||||
let Ok(llvm_range_elem_ty) = IntType::try_from(llvm_range_elem_ty) else {
|
|
||||||
return Err(format!(
|
|
||||||
"Expected int type for `range` element type, got {llvm_range_elem_ty}"
|
|
||||||
));
|
|
||||||
};
|
|
||||||
if llvm_range_elem_ty.get_bit_width() != 32 {
|
|
||||||
return Err(format!(
|
|
||||||
"Expected 32-bit int type for `range` element type, got {}",
|
|
||||||
llvm_range_elem_ty.get_bit_width()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
debug_assert!(Self::is_type(ptr_ty).is_ok());
|
|
||||||
|
|
||||||
RangeType { ty: ptr_ty }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the type of all fields of this `range` type.
|
|
||||||
#[must_use]
|
|
||||||
pub fn value_type(&self) -> IntType<'ctx> {
|
|
||||||
self.as_base_type().get_element_type().into_array_type().get_element_type().into_int_type()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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());
|
|
||||||
|
|
||||||
RangeValue { value, name }
|
|
||||||
}
|
|
||||||
|
|
||||||
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> {
|
|
||||||
fn from(value: RangeType<'ctx>) -> Self {
|
|
||||||
value.as_base_type()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Proxy type for accessing a `range` value in LLVM.
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct RangeValue<'ctx> {
|
|
||||||
value: PointerValue<'ctx>,
|
|
||||||
name: Option<&'ctx str>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> RangeValue<'ctx> {
|
|
||||||
/// Checks whether `value` is an instance of `range`, returning [Err] if `value` is not an instance.
|
|
||||||
pub fn is_instance(value: PointerValue<'ctx>) -> Result<(), String> {
|
|
||||||
RangeType::is_type(value.get_type())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an [`RangeValue`] from a [`PointerValue`].
|
|
||||||
#[must_use]
|
|
||||||
pub fn from_ptr_val(ptr: PointerValue<'ctx>, name: Option<&'ctx str>) -> Self {
|
|
||||||
debug_assert!(Self::is_instance(ptr).is_ok());
|
|
||||||
|
|
||||||
<Self as ProxyValue<'ctx>>::Type::from_type(ptr.get_type()).create_value(ptr, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ptr_to_start(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
|
|
||||||
let llvm_i32 = ctx.ctx.i32_type();
|
|
||||||
let var_name = self.name.map(|v| format!("{v}.start.addr")).unwrap_or_default();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
ctx.builder
|
|
||||||
.build_in_bounds_gep(
|
|
||||||
self.as_base_value(),
|
|
||||||
&[llvm_i32.const_zero(), llvm_i32.const_int(0, false)],
|
|
||||||
var_name.as_str(),
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ptr_to_end(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
|
|
||||||
let llvm_i32 = ctx.ctx.i32_type();
|
|
||||||
let var_name = self.name.map(|v| format!("{v}.end.addr")).unwrap_or_default();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
ctx.builder
|
|
||||||
.build_in_bounds_gep(
|
|
||||||
self.as_base_value(),
|
|
||||||
&[llvm_i32.const_zero(), llvm_i32.const_int(1, false)],
|
|
||||||
var_name.as_str(),
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ptr_to_step(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
|
|
||||||
let llvm_i32 = ctx.ctx.i32_type();
|
|
||||||
let var_name = self.name.map(|v| format!("{v}.step.addr")).unwrap_or_default();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
ctx.builder
|
|
||||||
.build_in_bounds_gep(
|
|
||||||
self.as_base_value(),
|
|
||||||
&[llvm_i32.const_zero(), llvm_i32.const_int(2, false)],
|
|
||||||
var_name.as_str(),
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stores the `start` value into this instance.
|
|
||||||
pub fn store_start(&self, ctx: &CodeGenContext<'ctx, '_>, start: IntValue<'ctx>) {
|
|
||||||
debug_assert_eq!(start.get_type().get_bit_width(), 32);
|
|
||||||
|
|
||||||
let pstart = self.ptr_to_start(ctx);
|
|
||||||
ctx.builder.build_store(pstart, start).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the `start` value of this `range`.
|
|
||||||
pub fn load_start(&self, ctx: &CodeGenContext<'ctx, '_>, name: Option<&str>) -> IntValue<'ctx> {
|
|
||||||
let pstart = self.ptr_to_start(ctx);
|
|
||||||
let var_name = name
|
|
||||||
.map(ToString::to_string)
|
|
||||||
.or_else(|| self.name.map(|v| format!("{v}.start")))
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
ctx.builder
|
|
||||||
.build_load(pstart, var_name.as_str())
|
|
||||||
.map(BasicValueEnum::into_int_value)
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stores the `end` value into this instance.
|
|
||||||
pub fn store_end(&self, ctx: &CodeGenContext<'ctx, '_>, end: IntValue<'ctx>) {
|
|
||||||
debug_assert_eq!(end.get_type().get_bit_width(), 32);
|
|
||||||
|
|
||||||
let pend = self.ptr_to_end(ctx);
|
|
||||||
ctx.builder.build_store(pend, end).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the `end` value of this `range`.
|
|
||||||
pub fn load_end(&self, ctx: &CodeGenContext<'ctx, '_>, name: Option<&str>) -> IntValue<'ctx> {
|
|
||||||
let pend = self.ptr_to_end(ctx);
|
|
||||||
let var_name = name
|
|
||||||
.map(ToString::to_string)
|
|
||||||
.or_else(|| self.name.map(|v| format!("{v}.end")))
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
ctx.builder.build_load(pend, var_name.as_str()).map(BasicValueEnum::into_int_value).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stores the `step` value into this instance.
|
|
||||||
pub fn store_step(&self, ctx: &CodeGenContext<'ctx, '_>, step: IntValue<'ctx>) {
|
|
||||||
debug_assert_eq!(step.get_type().get_bit_width(), 32);
|
|
||||||
|
|
||||||
let pstep = self.ptr_to_step(ctx);
|
|
||||||
ctx.builder.build_store(pstep, step).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the `step` value of this `range`.
|
|
||||||
pub fn load_step(&self, ctx: &CodeGenContext<'ctx, '_>, name: Option<&str>) -> IntValue<'ctx> {
|
|
||||||
let pstep = self.ptr_to_step(ctx);
|
|
||||||
let var_name = name
|
|
||||||
.map(ToString::to_string)
|
|
||||||
.or_else(|| self.name.map(|v| format!("{v}.step")))
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
ctx.builder
|
|
||||||
.build_load(pstep, var_name.as_str())
|
|
||||||
.map(BasicValueEnum::into_int_value)
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
RangeType::from_type(self.value.get_type())
|
|
||||||
}
|
|
||||||
|
|
||||||
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> {
|
|
||||||
fn from(value: RangeValue<'ctx>) -> Self {
|
|
||||||
value.as_base_value()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::{
|
||||||
codegen::{
|
codegen::{
|
||||||
classes::{
|
classes::{
|
||||||
ArrayLikeIndexer, ArrayLikeValue, ListType, ListValue, ProxyType, ProxyValue,
|
ArrayLikeIndexer, ArrayLikeValue, ListType, ListValue, ProxyType, ProxyValue,
|
||||||
RangeValue, UntypedArrayLikeAccessor,
|
UntypedArrayLikeAccessor,
|
||||||
},
|
},
|
||||||
concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore},
|
concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore},
|
||||||
gen_in_range_check, get_llvm_abi_type, get_llvm_type, get_va_count_arg_name,
|
gen_in_range_check, get_llvm_abi_type, get_llvm_type, get_va_count_arg_name,
|
||||||
|
@ -49,6 +49,7 @@ use super::{
|
||||||
object::{
|
object::{
|
||||||
any::AnyObject,
|
any::AnyObject,
|
||||||
ndarray::{indexing::util::gen_ndarray_subscript_ndindices, NDArrayObject},
|
ndarray::{indexing::util::gen_ndarray_subscript_ndindices, NDArrayObject},
|
||||||
|
range::RangeObject,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1080,18 +1081,6 @@ pub fn gen_call<'ctx, G: CodeGenerator>(
|
||||||
Ok(ctx.build_call_or_invoke(fun_val, ¶m_vals, "call"))
|
Ok(ctx.build_call_or_invoke(fun_val, ¶m_vals, "call"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates three LLVM variables representing the start, stop, and step values of a [range] class
|
|
||||||
/// respectively.
|
|
||||||
pub fn destructure_range<'ctx>(
|
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
|
||||||
range: RangeValue<'ctx>,
|
|
||||||
) -> (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>) {
|
|
||||||
let start = range.load_start(ctx, None);
|
|
||||||
let end = range.load_end(ctx, None);
|
|
||||||
let step = range.load_step(ctx, None);
|
|
||||||
(start, end, step)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Allocates a List structure with the given [type][ty] and [length]. The name of the resulting
|
/// Allocates a List structure with the given [type][ty] and [length]. The name of the resulting
|
||||||
/// LLVM value is `{name}.addr`, or `list.addr` if [name] is not specified.
|
/// LLVM value is `{name}.addr`, or `list.addr` if [name] is not specified.
|
||||||
///
|
///
|
||||||
|
@ -1166,8 +1155,14 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
|
||||||
TypeEnum::TObj { obj_id, .. }
|
TypeEnum::TObj { obj_id, .. }
|
||||||
if *obj_id == ctx.primitives.range.obj_id(&ctx.unifier).unwrap() =>
|
if *obj_id == ctx.primitives.range.obj_id(&ctx.unifier).unwrap() =>
|
||||||
{
|
{
|
||||||
let iter_val = RangeValue::from_ptr_val(iter_val.into_pointer_value(), Some("range"));
|
let range = AnyObject { value: iter_val, ty: iter_ty };
|
||||||
let (start, stop, step) = destructure_range(ctx, iter_val);
|
let range = RangeObject::from_object(generator, ctx, range);
|
||||||
|
|
||||||
|
let (start, stop, step) = range.instance.destructure(generator, ctx);
|
||||||
|
let start = start.value;
|
||||||
|
let stop = stop.value;
|
||||||
|
let step = step.value;
|
||||||
|
|
||||||
let diff = ctx.builder.build_int_sub(stop, start, "diff").unwrap();
|
let diff = ctx.builder.build_int_sub(stop, start, "diff").unwrap();
|
||||||
// add 1 to the length as the value is rounded to zero
|
// add 1 to the length as the value is rounded to zero
|
||||||
// the length may be 1 more than the actual length if the division is exact, but the
|
// the length may be 1 more than the actual length if the division is exact, but the
|
||||||
|
|
|
@ -609,6 +609,20 @@ pub fn setup_irrt_exceptions<'ctx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn call_nac3_range_len_i32<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
start: Instance<'ctx, Int<Int32>>,
|
||||||
|
stop: Instance<'ctx, Int<Int32>>,
|
||||||
|
step: Instance<'ctx, Int<Int32>>,
|
||||||
|
) -> Instance<'ctx, Int<Int32>> {
|
||||||
|
CallFunction::begin(generator, ctx, "__nac3_range_len_i32")
|
||||||
|
.arg(start)
|
||||||
|
.arg(stop)
|
||||||
|
.arg(step)
|
||||||
|
.returning_auto("range_len")
|
||||||
|
}
|
||||||
|
|
||||||
pub fn call_nac3_ndarray_util_assert_shape_no_negative<'ctx, G: CodeGenerator + ?Sized>(
|
pub fn call_nac3_ndarray_util_assert_shape_no_negative<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
codegen::classes::{ListType, ProxyType, RangeType},
|
codegen::classes::{ListType, ProxyType},
|
||||||
symbol_resolver::{StaticValue, SymbolResolver},
|
symbol_resolver::{StaticValue, SymbolResolver},
|
||||||
toplevel::{helper::PrimDef, TopLevelContext, TopLevelDef},
|
toplevel::{helper::PrimDef, TopLevelContext, TopLevelDef},
|
||||||
typecheck::{
|
typecheck::{
|
||||||
|
@ -26,7 +26,7 @@ use inkwell::{
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use model::*;
|
use model::*;
|
||||||
use nac3parser::ast::{Location, Stmt, StrRef};
|
use nac3parser::ast::{Location, Stmt, StrRef};
|
||||||
use object::ndarray::NDArray;
|
use object::{ndarray::NDArray, range::range_model};
|
||||||
use parking_lot::{Condvar, Mutex};
|
use parking_lot::{Condvar, Mutex};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::sync::{
|
use std::sync::{
|
||||||
|
@ -721,7 +721,7 @@ pub fn gen_func_impl<
|
||||||
Some(t) => t.as_basic_type_enum(),
|
Some(t) => t.as_basic_type_enum(),
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
(primitives.range, RangeType::new(context).as_base_type().into()),
|
(primitives.range, Ptr(range_model()).get_type(generator, context).into()),
|
||||||
(primitives.exception, {
|
(primitives.exception, {
|
||||||
let name = "Exception";
|
let name = "Exception";
|
||||||
if let Some(t) = module.get_struct_type(name) {
|
if let Some(t) = module.get_struct_type(name) {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod any;
|
pub mod any;
|
||||||
pub mod list;
|
pub mod list;
|
||||||
pub mod ndarray;
|
pub mod ndarray;
|
||||||
|
pub mod range;
|
||||||
pub mod tuple;
|
pub mod tuple;
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
use inkwell::{values::IntValue, IntPredicate};
|
||||||
|
|
||||||
|
use crate::codegen::{irrt::call_nac3_range_len_i32, model::*, CodeGenContext, CodeGenerator};
|
||||||
|
|
||||||
|
use super::any::AnyObject;
|
||||||
|
|
||||||
|
/// A range in NAC3.
|
||||||
|
pub type Range<N> = Array<Len<3>, Int<N>>;
|
||||||
|
|
||||||
|
/// An alias for `Range::<Int32>::default()`
|
||||||
|
#[must_use]
|
||||||
|
pub fn range_model() -> Range<Int32> {
|
||||||
|
Array::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx, N: IntKind<'ctx>> Instance<'ctx, Ptr<Range<N>>> {
|
||||||
|
/// Get GEP to `range.start`.
|
||||||
|
pub fn start(&self, ctx: &CodeGenContext<'ctx, '_>) -> Instance<'ctx, Ptr<Int<N>>> {
|
||||||
|
self.gep_const(ctx, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get GEP to `range.stop`.
|
||||||
|
pub fn stop(&self, ctx: &CodeGenContext<'ctx, '_>) -> Instance<'ctx, Ptr<Int<N>>> {
|
||||||
|
self.gep_const(ctx, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get GEP to `range.step`.
|
||||||
|
pub fn step(&self, ctx: &CodeGenContext<'ctx, '_>) -> Instance<'ctx, Ptr<Int<N>>> {
|
||||||
|
self.gep_const(ctx, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience function to load the `(start, stop, step)` of this range.
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
pub fn destructure<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
) -> (Instance<'ctx, Int<N>>, Instance<'ctx, Int<N>>, Instance<'ctx, Int<N>>) {
|
||||||
|
let start = self.start(ctx).load(generator, ctx);
|
||||||
|
let stop = self.stop(ctx).load(generator, ctx);
|
||||||
|
let step = self.step(ctx).load(generator, ctx);
|
||||||
|
(start, stop, step)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate LLVM IR to check that a range's `step` is not zero.
|
||||||
|
/// Throws "range step must not be zero" if it is the case.
|
||||||
|
pub fn assert_range_step_non_zero<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
step: IntValue<'ctx>,
|
||||||
|
) {
|
||||||
|
let int32 = ctx.ctx.i32_type();
|
||||||
|
let rangenez =
|
||||||
|
ctx.builder.build_int_compare(IntPredicate::NE, step, int32.const_zero(), "").unwrap();
|
||||||
|
ctx.make_assert(
|
||||||
|
generator,
|
||||||
|
rangenez,
|
||||||
|
"0:ValueError",
|
||||||
|
"range step must not be zero",
|
||||||
|
[None, None, None],
|
||||||
|
ctx.current_loc,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: `RangeObject` in the future will have range32, range64
|
||||||
|
|
||||||
|
/// A NAC3 Python range object.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct RangeObject<'ctx> {
|
||||||
|
pub instance: Instance<'ctx, Ptr<Range<Int32>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx> RangeObject<'ctx> {
|
||||||
|
/// Attempt to convert an [`AnyObject`] into a [`RangeObject`].
|
||||||
|
pub fn from_object<G: CodeGenerator + ?Sized>(
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
object: AnyObject<'ctx>,
|
||||||
|
) -> RangeObject<'ctx> {
|
||||||
|
assert!(ctx.unifier.unioned(object.ty, ctx.primitives.range));
|
||||||
|
|
||||||
|
let instance = Ptr(Range::default()).check_value(generator, ctx.ctx, object.value).unwrap();
|
||||||
|
RangeObject { instance }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the `len()` of this range.
|
||||||
|
pub fn len<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
) -> Instance<'ctx, Int<Int32>> {
|
||||||
|
let (start, stop, step) = self.instance.destructure(generator, ctx);
|
||||||
|
assert_range_step_non_zero(generator, ctx, step.value);
|
||||||
|
call_nac3_range_len_i32(generator, ctx, start, stop, step)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +1,18 @@
|
||||||
use super::{
|
use super::{
|
||||||
super::symbol_resolver::ValueEnum,
|
super::symbol_resolver::ValueEnum,
|
||||||
expr::destructure_range,
|
|
||||||
irrt::{handle_slice_indices, list_slice_assignment},
|
irrt::{handle_slice_indices, list_slice_assignment},
|
||||||
object::{
|
object::{
|
||||||
any::AnyObject,
|
any::AnyObject,
|
||||||
ndarray::{
|
ndarray::{
|
||||||
indexing::util::gen_ndarray_subscript_ndindices, NDArrayObject, ScalarOrNDArray,
|
indexing::util::gen_ndarray_subscript_ndindices, NDArrayObject, ScalarOrNDArray,
|
||||||
},
|
},
|
||||||
|
range::RangeObject,
|
||||||
},
|
},
|
||||||
CodeGenContext, CodeGenerator,
|
CodeGenContext, CodeGenerator,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
codegen::{
|
codegen::{
|
||||||
classes::{ArrayLikeIndexer, ArraySliceValue, ListValue, RangeValue},
|
classes::{ArrayLikeIndexer, ArraySliceValue, ListValue},
|
||||||
expr::gen_binop_expr,
|
expr::gen_binop_expr,
|
||||||
gen_in_range_check,
|
gen_in_range_check,
|
||||||
},
|
},
|
||||||
|
@ -497,7 +497,14 @@ pub fn gen_for<G: CodeGenerator>(
|
||||||
TypeEnum::TObj { obj_id, .. }
|
TypeEnum::TObj { obj_id, .. }
|
||||||
if *obj_id == ctx.primitives.range.obj_id(&ctx.unifier).unwrap() =>
|
if *obj_id == ctx.primitives.range.obj_id(&ctx.unifier).unwrap() =>
|
||||||
{
|
{
|
||||||
let iter_val = RangeValue::from_ptr_val(iter_val.into_pointer_value(), Some("range"));
|
let range = AnyObject { value: iter_val, ty: iter_ty };
|
||||||
|
let range = RangeObject::from_object(generator, ctx, range);
|
||||||
|
|
||||||
|
let (start, stop, step) = range.instance.destructure(generator, ctx);
|
||||||
|
let start = start.value;
|
||||||
|
let stop = stop.value;
|
||||||
|
let step = step.value;
|
||||||
|
|
||||||
// Internal variable for loop; Cannot be assigned
|
// Internal variable for loop; Cannot be assigned
|
||||||
let i = generator.gen_var_alloc(ctx, int32.into(), Some("for.i.addr"))?;
|
let i = generator.gen_var_alloc(ctx, int32.into(), Some("for.i.addr"))?;
|
||||||
// Variable declared in "target" expression of the loop; Can be reassigned *or* shadowed
|
// Variable declared in "target" expression of the loop; Can be reassigned *or* shadowed
|
||||||
|
@ -506,7 +513,6 @@ pub fn gen_for<G: CodeGenerator>(
|
||||||
else {
|
else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
let (start, stop, step) = destructure_range(ctx, iter_val);
|
|
||||||
|
|
||||||
ctx.builder.build_store(i, start).unwrap();
|
ctx.builder.build_store(i, start).unwrap();
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
codegen::{
|
codegen::{
|
||||||
classes::{ListType, ProxyType, RangeType},
|
classes::{ListType, ProxyType},
|
||||||
concrete_type::ConcreteTypeStore,
|
concrete_type::ConcreteTypeStore,
|
||||||
CodeGenContext, CodeGenLLVMOptions, CodeGenTargetMachineOptions, CodeGenTask,
|
CodeGenContext, CodeGenLLVMOptions, CodeGenTargetMachineOptions, CodeGenTask,
|
||||||
CodeGenerator, DefaultCodeGenerator, WithCall, WorkerRegistry,
|
CodeGenerator, DefaultCodeGenerator, WithCall, WorkerRegistry,
|
||||||
|
@ -448,11 +448,3 @@ fn test_classes_list_type_new() {
|
||||||
let llvm_list = ListType::new(&generator, &ctx, llvm_i32.into());
|
let llvm_list = ListType::new(&generator, &ctx, llvm_i32.into());
|
||||||
assert!(ListType::is_type(llvm_list.as_base_type(), llvm_usize).is_ok());
|
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());
|
|
||||||
}
|
|
||||||
|
|
|
@ -15,12 +15,12 @@ use strum::IntoEnumIterator;
|
||||||
use crate::{
|
use crate::{
|
||||||
codegen::{
|
codegen::{
|
||||||
builtin_fns,
|
builtin_fns,
|
||||||
classes::{ProxyValue, RangeValue},
|
|
||||||
model::*,
|
model::*,
|
||||||
numpy::*,
|
numpy::*,
|
||||||
object::{
|
object::{
|
||||||
any::AnyObject,
|
any::AnyObject,
|
||||||
ndarray::{shape_util::parse_numpy_int_sequence, NDArrayObject},
|
ndarray::{shape_util::parse_numpy_int_sequence, NDArrayObject},
|
||||||
|
range::RangeObject,
|
||||||
},
|
},
|
||||||
stmt::exn_constructor,
|
stmt::exn_constructor,
|
||||||
},
|
},
|
||||||
|
@ -717,9 +717,10 @@ impl<'a> BuiltinBuilder<'a> {
|
||||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||||
|ctx, obj, _, args, generator| {
|
|ctx, obj, _, args, generator| {
|
||||||
let (zelf_ty, zelf) = obj.unwrap();
|
let (zelf_ty, zelf) = obj.unwrap();
|
||||||
let zelf =
|
let zelf = zelf.to_basic_value_enum(ctx, generator, zelf_ty)?;
|
||||||
zelf.to_basic_value_enum(ctx, generator, zelf_ty)?.into_pointer_value();
|
|
||||||
let zelf = RangeValue::from_ptr_val(zelf, Some("range"));
|
let zelf = AnyObject { ty: zelf_ty, value: zelf };
|
||||||
|
let zelf = RangeObject::from_object(generator, ctx, zelf);
|
||||||
|
|
||||||
let mut start = None;
|
let mut start = None;
|
||||||
let mut stop = None;
|
let mut stop = None;
|
||||||
|
@ -802,11 +803,14 @@ impl<'a> BuiltinBuilder<'a> {
|
||||||
});
|
});
|
||||||
let start = start.unwrap_or_else(|| int32.const_zero());
|
let start = start.unwrap_or_else(|| int32.const_zero());
|
||||||
|
|
||||||
zelf.store_start(ctx, start);
|
let start = Int(Int32).believe_value(start);
|
||||||
zelf.store_end(ctx, stop);
|
let stop = Int(Int32).believe_value(stop);
|
||||||
zelf.store_step(ctx, step);
|
let step = Int(Int32).believe_value(step);
|
||||||
|
zelf.instance.start(ctx).store(ctx, start);
|
||||||
|
zelf.instance.stop(ctx).store(ctx, stop);
|
||||||
|
zelf.instance.step(ctx).store(ctx, step);
|
||||||
|
|
||||||
Ok(Some(zelf.as_base_value().into()))
|
Ok(Some(zelf.instance.value.as_basic_value_enum()))
|
||||||
},
|
},
|
||||||
)))),
|
)))),
|
||||||
loc: None,
|
loc: None,
|
||||||
|
|
Loading…
Reference in New Issue