From 6528115d6ae28f3c45603338a346981ee14a08c2 Mon Sep 17 00:00:00 2001 From: lyken Date: Sun, 25 Aug 2024 14:45:22 +0800 Subject: [PATCH] core: refactor range with model and RangeObject --- nac3artiq/src/codegen.rs | 14 +- nac3core/irrt/irrt/range.hpp | 8 + nac3core/irrt/irrt/slice.hpp | 2 +- nac3core/src/codegen/builtin_fns.rs | 71 ++++--- nac3core/src/codegen/classes.rs | 267 +-------------------------- nac3core/src/codegen/expr.rs | 25 +-- nac3core/src/codegen/irrt/mod.rs | 14 ++ nac3core/src/codegen/mod.rs | 6 +- nac3core/src/codegen/object/mod.rs | 1 + nac3core/src/codegen/object/range.rs | 97 ++++++++++ nac3core/src/codegen/stmt.rs | 14 +- nac3core/src/codegen/test.rs | 10 +- nac3core/src/toplevel/builtins.rs | 20 +- 13 files changed, 202 insertions(+), 347 deletions(-) create mode 100644 nac3core/src/codegen/object/range.rs diff --git a/nac3artiq/src/codegen.rs b/nac3artiq/src/codegen.rs index e4a0d30f..cca41bbf 100644 --- a/nac3artiq/src/codegen.rs +++ b/nac3artiq/src/codegen.rs @@ -1,10 +1,10 @@ use nac3core::{ codegen::{ - classes::{ListValue, RangeValue, UntypedArrayLikeAccessor}, - expr::{destructure_range, gen_call}, + classes::{ListValue, UntypedArrayLikeAccessor}, + expr::gen_call, llvm_intrinsics::{call_int_smax, call_stackrestore, call_stacksave}, 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}, CodeGenContext, CodeGenerator, }, @@ -1121,9 +1121,13 @@ fn polymorphic_print<'ctx>( fmt.push_str("range("); 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( ctx, diff --git a/nac3core/irrt/irrt/range.hpp b/nac3core/irrt/irrt/range.hpp index 8f115d0e..651cc597 100644 --- a/nac3core/irrt/irrt/range.hpp +++ b/nac3core/irrt/irrt/range.hpp @@ -39,3 +39,11 @@ template struct Range } }; } // namespace + +extern "C" +{ + int32_t __nac3_range_len_i32(int32_t start, int32_t stop, int32_t step) + { + return range::len(start, stop, step); + } +} \ No newline at end of file diff --git a/nac3core/irrt/irrt/slice.hpp b/nac3core/irrt/irrt/slice.hpp index 35b754ef..0e56362f 100644 --- a/nac3core/irrt/irrt/slice.hpp +++ b/nac3core/irrt/irrt/slice.hpp @@ -180,4 +180,4 @@ template struct Slice return this->indices(length); } }; -} // namespace +} // namespace \ No newline at end of file diff --git a/nac3core/src/codegen/builtin_fns.rs b/nac3core/src/codegen/builtin_fns.rs index 53dd93b0..dfb34256 100644 --- a/nac3core/src/codegen/builtin_fns.rs +++ b/nac3core/src/codegen/builtin_fns.rs @@ -3,19 +3,17 @@ use inkwell::values::{BasicValue, BasicValueEnum, IntValue}; use inkwell::{FloatPredicate, IntPredicate, OptimizationLevel}; 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::object::any::AnyObject; use super::object::list::ListObject; use super::object::ndarray::NDArrayObject; 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. /// @@ -31,36 +29,35 @@ fn unsupported_type(ctx: &CodeGenContext<'_, '_>, fn_name: &str, tys: &[Type]) - pub fn call_len<'ctx, G: CodeGenerator + ?Sized>( generator: &mut G, ctx: &mut CodeGenContext<'ctx, '_>, - n: (Type, BasicValueEnum<'ctx>), + (arg_ty, arg): (Type, BasicValueEnum<'ctx>), ) -> Result, 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 len: Instance<'ctx, Int> = match &*ctx.unifier.get_ty(arg_ty) { - TypeEnum::TTuple { .. } => { - let tuple = TupleObject::from_object(ctx, arg); - tuple.len(generator, ctx).truncate_or_bit_cast(generator, ctx, Int32) - } - TypeEnum::TObj { obj_id, .. } - if *obj_id == ctx.primitives.ndarray.obj_id(&ctx.unifier).unwrap() => - { - let ndarray = NDArrayObject::from_object(generator, ctx, arg); - ndarray.len(generator, ctx).truncate_or_bit_cast(generator, ctx, Int32) - } - TypeEnum::TObj { obj_id, .. } - if *obj_id == ctx.primitives.list.obj_id(&ctx.unifier).unwrap() => - { - let list = ListObject::from_object(generator, ctx, arg); - list.len(generator, ctx).truncate_or_bit_cast(generator, ctx, Int32) - } - _ => unsupported_type(ctx, "len", &[arg_ty]), - }; - len.value - }) + let arg = AnyObject { ty: arg_ty, value: arg }; + let len: Instance<'ctx, Int> = match &*ctx.unifier.get_ty(arg_ty) { + TypeEnum::TTuple { .. } => { + let tuple = TupleObject::from_object(ctx, arg); + 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, .. } + if *obj_id == ctx.primitives.ndarray.obj_id(&ctx.unifier).unwrap() => + { + let ndarray = NDArrayObject::from_object(generator, ctx, arg); + ndarray.len(generator, ctx).truncate_or_bit_cast(generator, ctx, Int32) + } + TypeEnum::TObj { obj_id, .. } + if *obj_id == ctx.primitives.list.obj_id(&ctx.unifier).unwrap() => + { + let list = ListObject::from_object(generator, ctx, arg); + list.len(generator, ctx).truncate_or_bit_cast(generator, ctx, Int32) + } + _ => unsupported_type(ctx, "len", &[arg_ty]), + }; + Ok(len.value) } /// Invokes the `int32` builtin function. diff --git a/nac3core/src/codegen/classes.rs b/nac3core/src/codegen/classes.rs index 42676404..d06b2c15 100644 --- a/nac3core/src/codegen/classes.rs +++ b/nac3core/src/codegen/classes.rs @@ -1,7 +1,7 @@ use crate::codegen::{CodeGenContext, CodeGenerator}; use inkwell::context::Context; -use inkwell::types::{ArrayType, BasicType, StructType}; -use inkwell::values::{ArrayValue, BasicValue, StructValue}; +use inkwell::types::{BasicType, StructType}; +use inkwell::values::{BasicValue, StructValue}; use inkwell::{ types::{AnyTypeEnum, BasicTypeEnum, IntType, PointerType}, values::{BasicValueEnum, IntValue, PointerValue}, @@ -873,266 +873,3 @@ impl<'ctx> ArrayLikeIndexer<'ctx> for ListDataProxy<'ctx, '_> { impl<'ctx> UntypedArrayLikeAccessor<'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( - &self, - generator: &mut G, - ctx: &mut CodeGenContext<'ctx, '_>, - name: Option<&'ctx str>, - ) -> Self::Value { - self.create_value( - generator.gen_var_alloc(ctx, self.as_underlying_type().into(), name).unwrap(), - name, - ) - } - - fn create_value( - &self, - value: >::Base, - name: Option<&'ctx str>, - ) -> Self::Value { - debug_assert_eq!(value.get_type(), self.as_base_type()); - - 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> 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()); - - >::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> for PointerValue<'ctx> { - fn from(value: RangeValue<'ctx>) -> Self { - value.as_base_value() - } -} diff --git a/nac3core/src/codegen/expr.rs b/nac3core/src/codegen/expr.rs index 2c8e3b52..47437688 100644 --- a/nac3core/src/codegen/expr.rs +++ b/nac3core/src/codegen/expr.rs @@ -2,7 +2,7 @@ use crate::{ codegen::{ classes::{ ArrayLikeIndexer, ArrayLikeValue, ListType, ListValue, ProxyType, ProxyValue, - RangeValue, UntypedArrayLikeAccessor, + UntypedArrayLikeAccessor, }, concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore}, gen_in_range_check, get_llvm_abi_type, get_llvm_type, get_va_count_arg_name, @@ -49,6 +49,7 @@ use super::{ object::{ any::AnyObject, 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")) } -/// 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 /// 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, .. } 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 (start, stop, step) = destructure_range(ctx, iter_val); + 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; + let diff = ctx.builder.build_int_sub(stop, start, "diff").unwrap(); // 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 diff --git a/nac3core/src/codegen/irrt/mod.rs b/nac3core/src/codegen/irrt/mod.rs index 3acbb08c..c7fe3e01 100644 --- a/nac3core/src/codegen/irrt/mod.rs +++ b/nac3core/src/codegen/irrt/mod.rs @@ -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>, + stop: Instance<'ctx, Int>, + step: Instance<'ctx, Int>, +) -> Instance<'ctx, Int> { + 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>( generator: &mut G, ctx: &mut CodeGenContext<'ctx, '_>, diff --git a/nac3core/src/codegen/mod.rs b/nac3core/src/codegen/mod.rs index b90cb59b..d50a1bf7 100644 --- a/nac3core/src/codegen/mod.rs +++ b/nac3core/src/codegen/mod.rs @@ -1,5 +1,5 @@ use crate::{ - codegen::classes::{ListType, ProxyType, RangeType}, + codegen::classes::{ListType, ProxyType}, symbol_resolver::{StaticValue, SymbolResolver}, toplevel::{helper::PrimDef, TopLevelContext, TopLevelDef}, typecheck::{ @@ -26,7 +26,7 @@ use inkwell::{ use itertools::Itertools; use model::*; use nac3parser::ast::{Location, Stmt, StrRef}; -use object::ndarray::NDArray; +use object::{ndarray::NDArray, range::range_model}; use parking_lot::{Condvar, Mutex}; use std::collections::{HashMap, HashSet}; use std::sync::{ @@ -721,7 +721,7 @@ pub fn gen_func_impl< 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, { let name = "Exception"; if let Some(t) = module.get_struct_type(name) { diff --git a/nac3core/src/codegen/object/mod.rs b/nac3core/src/codegen/object/mod.rs index 9466d70b..c3bade4b 100644 --- a/nac3core/src/codegen/object/mod.rs +++ b/nac3core/src/codegen/object/mod.rs @@ -1,4 +1,5 @@ pub mod any; pub mod list; pub mod ndarray; +pub mod range; pub mod tuple; diff --git a/nac3core/src/codegen/object/range.rs b/nac3core/src/codegen/object/range.rs new file mode 100644 index 00000000..5ca84fca --- /dev/null +++ b/nac3core/src/codegen/object/range.rs @@ -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 = Array, Int>; + +/// An alias for `Range::::default()` +#[must_use] +pub fn range_model() -> Range { + Array::default() +} + +impl<'ctx, N: IntKind<'ctx>> Instance<'ctx, Ptr>> { + /// Get GEP to `range.start`. + pub fn start(&self, ctx: &CodeGenContext<'ctx, '_>) -> Instance<'ctx, Ptr>> { + self.gep_const(ctx, 0) + } + + /// Get GEP to `range.stop`. + pub fn stop(&self, ctx: &CodeGenContext<'ctx, '_>) -> Instance<'ctx, Ptr>> { + self.gep_const(ctx, 1) + } + + /// Get GEP to `range.step`. + pub fn step(&self, ctx: &CodeGenContext<'ctx, '_>) -> Instance<'ctx, Ptr>> { + self.gep_const(ctx, 2) + } + + /// Convenience function to load the `(start, stop, step)` of this range. + #[allow(clippy::type_complexity)] + pub fn destructure( + &self, + generator: &mut G, + ctx: &CodeGenContext<'ctx, '_>, + ) -> (Instance<'ctx, Int>, Instance<'ctx, Int>, Instance<'ctx, Int>) { + 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>>, +} + +impl<'ctx> RangeObject<'ctx> { + /// Attempt to convert an [`AnyObject`] into a [`RangeObject`]. + pub fn from_object( + 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( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + ) -> Instance<'ctx, Int> { + 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) + } +} diff --git a/nac3core/src/codegen/stmt.rs b/nac3core/src/codegen/stmt.rs index 7e053c06..115ca636 100644 --- a/nac3core/src/codegen/stmt.rs +++ b/nac3core/src/codegen/stmt.rs @@ -1,18 +1,18 @@ use super::{ super::symbol_resolver::ValueEnum, - expr::destructure_range, irrt::{handle_slice_indices, list_slice_assignment}, object::{ any::AnyObject, ndarray::{ indexing::util::gen_ndarray_subscript_ndindices, NDArrayObject, ScalarOrNDArray, }, + range::RangeObject, }, CodeGenContext, CodeGenerator, }; use crate::{ codegen::{ - classes::{ArrayLikeIndexer, ArraySliceValue, ListValue, RangeValue}, + classes::{ArrayLikeIndexer, ArraySliceValue, ListValue}, expr::gen_binop_expr, gen_in_range_check, }, @@ -497,7 +497,14 @@ pub fn gen_for( TypeEnum::TObj { obj_id, .. } 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 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 @@ -506,7 +513,6 @@ pub fn gen_for( else { unreachable!() }; - let (start, stop, step) = destructure_range(ctx, iter_val); ctx.builder.build_store(i, start).unwrap(); diff --git a/nac3core/src/codegen/test.rs b/nac3core/src/codegen/test.rs index 09c99553..0534a003 100644 --- a/nac3core/src/codegen/test.rs +++ b/nac3core/src/codegen/test.rs @@ -1,6 +1,6 @@ use crate::{ codegen::{ - classes::{ListType, ProxyType, RangeType}, + classes::{ListType, ProxyType}, concrete_type::ConcreteTypeStore, CodeGenContext, CodeGenLLVMOptions, CodeGenTargetMachineOptions, CodeGenTask, CodeGenerator, DefaultCodeGenerator, WithCall, WorkerRegistry, @@ -448,11 +448,3 @@ fn test_classes_list_type_new() { let llvm_list = ListType::new(&generator, &ctx, llvm_i32.into()); assert!(ListType::is_type(llvm_list.as_base_type(), llvm_usize).is_ok()); } - -#[test] -fn test_classes_range_type_new() { - let ctx = inkwell::context::Context::create(); - - let llvm_range = RangeType::new(&ctx); - assert!(RangeType::is_type(llvm_range.as_base_type()).is_ok()); -} diff --git a/nac3core/src/toplevel/builtins.rs b/nac3core/src/toplevel/builtins.rs index 1a810f28..7acf487a 100644 --- a/nac3core/src/toplevel/builtins.rs +++ b/nac3core/src/toplevel/builtins.rs @@ -15,12 +15,12 @@ use strum::IntoEnumIterator; use crate::{ codegen::{ builtin_fns, - classes::{ProxyValue, RangeValue}, model::*, numpy::*, object::{ any::AnyObject, ndarray::{shape_util::parse_numpy_int_sequence, NDArrayObject}, + range::RangeObject, }, stmt::exn_constructor, }, @@ -717,9 +717,10 @@ impl<'a> BuiltinBuilder<'a> { codegen_callback: Some(Arc::new(GenCall::new(Box::new( |ctx, obj, _, args, generator| { let (zelf_ty, zelf) = obj.unwrap(); - let zelf = - zelf.to_basic_value_enum(ctx, generator, zelf_ty)?.into_pointer_value(); - let zelf = RangeValue::from_ptr_val(zelf, Some("range")); + let zelf = zelf.to_basic_value_enum(ctx, generator, zelf_ty)?; + + let zelf = AnyObject { ty: zelf_ty, value: zelf }; + let zelf = RangeObject::from_object(generator, ctx, zelf); let mut start = None; let mut stop = None; @@ -802,11 +803,14 @@ impl<'a> BuiltinBuilder<'a> { }); let start = start.unwrap_or_else(|| int32.const_zero()); - zelf.store_start(ctx, start); - zelf.store_end(ctx, stop); - zelf.store_step(ctx, step); + let start = Int(Int32).believe_value(start); + let stop = Int(Int32).believe_value(stop); + 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,