From fdfc80ca5f03f41d87dadad46df75f0fbb508f4d Mon Sep 17 00:00:00 2001 From: lyken Date: Sat, 24 Aug 2024 15:37:45 +0800 Subject: [PATCH] [core] codegen: Implement Slice{Type,Value}, RustSlice Based on 01c96396: core/irrt: add Slice and Range and part of 8f9d2d82: core/ndstrides: implement ndarray indexing. Needed for implementing general ndarray indexing. Currently IRRT slice and range have nothing to do with NAC3's slice and range. The IRRT slice and range are currently there to implement ndarray specific features. However, in the future their definitions may be used to replace that of NAC3's. (NAC3's range is a [i32 x 3], IRRT's range is a proper struct. NAC3 does not have a slice struct). --- nac3core/irrt/irrt.cpp | 1 + nac3core/irrt/irrt/range.hpp | 47 ++++ nac3core/irrt/irrt/slice.hpp | 150 +++++++++++- nac3core/src/codegen/irrt/mod.rs | 2 + nac3core/src/codegen/irrt/range.rs | 42 ++++ nac3core/src/codegen/irrt/slice.rs | 41 +--- nac3core/src/codegen/types/mod.rs | 1 + nac3core/src/codegen/types/utils/mod.rs | 3 + nac3core/src/codegen/types/utils/slice.rs | 254 +++++++++++++++++++++ nac3core/src/codegen/values/mod.rs | 1 + nac3core/src/codegen/values/utils/mod.rs | 3 + nac3core/src/codegen/values/utils/slice.rs | 231 +++++++++++++++++++ 12 files changed, 726 insertions(+), 50 deletions(-) create mode 100644 nac3core/irrt/irrt/range.hpp create mode 100644 nac3core/src/codegen/irrt/range.rs create mode 100644 nac3core/src/codegen/types/utils/mod.rs create mode 100644 nac3core/src/codegen/types/utils/slice.rs create mode 100644 nac3core/src/codegen/values/utils/mod.rs create mode 100644 nac3core/src/codegen/values/utils/slice.rs diff --git a/nac3core/irrt/irrt.cpp b/nac3core/irrt/irrt.cpp index 1093e8e071..5eafe0135f 100644 --- a/nac3core/irrt/irrt.cpp +++ b/nac3core/irrt/irrt.cpp @@ -2,6 +2,7 @@ #include "irrt/list.hpp" #include "irrt/math.hpp" #include "irrt/ndarray.hpp" +#include "irrt/range.hpp" #include "irrt/slice.hpp" #include "irrt/ndarray/basic.hpp" #include "irrt/ndarray/def.hpp" diff --git a/nac3core/irrt/irrt/range.hpp b/nac3core/irrt/irrt/range.hpp new file mode 100644 index 0000000000..e9d4e612c8 --- /dev/null +++ b/nac3core/irrt/irrt/range.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include "irrt/debug.hpp" +#include "irrt/int_types.hpp" + +namespace { +namespace range { +template +T len(T start, T stop, T step) { + // Reference: + // https://github.com/python/cpython/blob/9dbd12375561a393eaec4b21ee4ac568a407cdb0/Objects/rangeobject.c#L933 + if (step > 0 && start < stop) + return 1 + (stop - 1 - start) / step; + else if (step < 0 && start > stop) + return 1 + (start - 1 - stop) / (-step); + else + return 0; +} +} // namespace range + +/** + * @brief A Python range. + */ +template +struct Range { + T start; + T stop; + T step; + + /** + * @brief Calculate the `len()` of this range. + */ + template + T len() { + debug_assert(SizeT, step != 0); + return range::len(start, stop, step); + } +}; +} // namespace + +extern "C" { +using namespace range; + +SliceIndex __nac3_range_slice_len(const SliceIndex start, const SliceIndex end, const SliceIndex step) { + return len(start, end, step); +} +} \ No newline at end of file diff --git a/nac3core/irrt/irrt/slice.hpp b/nac3core/irrt/irrt/slice.hpp index 3f6d83a3d5..4cf13e05e4 100644 --- a/nac3core/irrt/irrt/slice.hpp +++ b/nac3core/irrt/irrt/slice.hpp @@ -1,6 +1,145 @@ #pragma once +#include "irrt/debug.hpp" +#include "irrt/exception.hpp" #include "irrt/int_types.hpp" +#include "irrt/math_util.hpp" +#include "irrt/range.hpp" + +namespace { +namespace slice { +/** + * @brief Resolve a possibly negative index in a list of a known length. + * + * Returns -1 if the resolved index is out of the list's bounds. + */ +template +T resolve_index_in_length(T length, T index) { + T resolved = index < 0 ? length + index : index; + if (0 <= resolved && resolved < length) { + return resolved; + } else { + return -1; + } +} + +/** + * @brief Resolve a slice as a range. + * + * This is equivalent to `range(*slice(start, stop, step).indices(length))` in Python. + */ +template +void indices(bool start_defined, + T start, + bool stop_defined, + T stop, + bool step_defined, + T step, + T length, + T* range_start, + T* range_stop, + T* range_step) { + // Reference: https://github.com/python/cpython/blob/main/Objects/sliceobject.c#L388 + *range_step = step_defined ? step : 1; + bool step_is_negative = *range_step < 0; + + T lower, upper; + if (step_is_negative) { + lower = -1; + upper = length - 1; + } else { + lower = 0; + upper = length; + } + + if (start_defined) { + *range_start = start < 0 ? max(lower, start + length) : min(upper, start); + } else { + *range_start = step_is_negative ? upper : lower; + } + + if (stop_defined) { + *range_stop = stop < 0 ? max(lower, stop + length) : min(upper, stop); + } else { + *range_stop = step_is_negative ? lower : upper; + } +} +} // namespace slice + +/** + * @brief A Python-like slice with **unresolved** indices. + */ +template +struct Slice { + bool start_defined; + T start; + + bool stop_defined; + T stop; + + bool step_defined; + T step; + + Slice() { this->reset(); } + + void reset() { + this->start_defined = false; + this->stop_defined = false; + this->step_defined = false; + } + + void set_start(T start) { + this->start_defined = true; + this->start = start; + } + + void set_stop(T stop) { + this->stop_defined = true; + this->stop = stop; + } + + void set_step(T step) { + this->step_defined = true; + this->step = step; + } + + /** + * @brief Resolve this slice as a range. + * + * In Python, this would be `range(*slice(start, stop, step).indices(length))`. + */ + template + Range indices(T length) { + // Reference: + // https://github.com/python/cpython/blob/main/Objects/sliceobject.c#L388 + debug_assert(SizeT, length >= 0); + + Range result; + slice::indices(start_defined, start, stop_defined, stop, step_defined, step, length, &result.start, + &result.stop, &result.step); + return result; + } + + /** + * @brief Like `.indices()` but with assertions. + */ + template + Range indices_checked(T length) { + // TODO: Switch to `SizeT length` + + if (length < 0) { + raise_exception(SizeT, EXN_VALUE_ERROR, "length should not be negative, got {0}", length, NO_PARAM, + NO_PARAM); + } + + if (this->step_defined && this->step == 0) { + raise_exception(SizeT, EXN_VALUE_ERROR, "slice step cannot be zero", NO_PARAM, NO_PARAM, NO_PARAM); + } + + return this->indices(length); + } +}; +} // namespace extern "C" { SliceIndex __nac3_slice_index_bound(SliceIndex i, const SliceIndex len) { @@ -14,15 +153,4 @@ SliceIndex __nac3_slice_index_bound(SliceIndex i, const SliceIndex len) { } return i; } - -SliceIndex __nac3_range_slice_len(const SliceIndex start, const SliceIndex end, const SliceIndex step) { - SliceIndex diff = end - start; - if (diff > 0 && step > 0) { - return ((diff - 1) / step) + 1; - } else if (diff < 0 && step < 0) { - return ((diff + 1) / step) + 1; - } else { - return 0; - } } -} // namespace \ No newline at end of file diff --git a/nac3core/src/codegen/irrt/mod.rs b/nac3core/src/codegen/irrt/mod.rs index ed922db5d9..824921cd96 100644 --- a/nac3core/src/codegen/irrt/mod.rs +++ b/nac3core/src/codegen/irrt/mod.rs @@ -13,11 +13,13 @@ use super::{CodeGenContext, CodeGenerator}; use crate::{symbol_resolver::SymbolResolver, typecheck::typedef::Type}; pub use list::*; pub use math::*; +pub use range::*; pub use slice::*; mod list; mod math; pub mod ndarray; +mod range; mod slice; #[must_use] diff --git a/nac3core/src/codegen/irrt/range.rs b/nac3core/src/codegen/irrt/range.rs new file mode 100644 index 0000000000..47c63c4fbf --- /dev/null +++ b/nac3core/src/codegen/irrt/range.rs @@ -0,0 +1,42 @@ +use inkwell::{ + values::{BasicValueEnum, CallSiteValue, IntValue}, + IntPredicate, +}; +use itertools::Either; + +use crate::codegen::{CodeGenContext, CodeGenerator}; + +pub fn calculate_len_for_slice_range<'ctx, G: CodeGenerator + ?Sized>( + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + start: IntValue<'ctx>, + end: IntValue<'ctx>, + step: IntValue<'ctx>, +) -> IntValue<'ctx> { + const SYMBOL: &str = "__nac3_range_slice_len"; + let len_func = ctx.module.get_function(SYMBOL).unwrap_or_else(|| { + let i32_t = ctx.ctx.i32_type(); + let fn_t = i32_t.fn_type(&[i32_t.into(), i32_t.into(), i32_t.into()], false); + ctx.module.add_function(SYMBOL, fn_t, None) + }); + + // assert step != 0, throw exception if not + let not_zero = ctx + .builder + .build_int_compare(IntPredicate::NE, step, step.get_type().const_zero(), "range_step_ne") + .unwrap(); + ctx.make_assert( + generator, + not_zero, + "0:ValueError", + "step must not be zero", + [None, None, None], + ctx.current_loc, + ); + ctx.builder + .build_call(len_func, &[start.into(), end.into(), step.into()], "calc_len") + .map(CallSiteValue::try_as_basic_value) + .map(|v| v.map_left(BasicValueEnum::into_int_value)) + .map(Either::unwrap_left) + .unwrap() +} diff --git a/nac3core/src/codegen/irrt/slice.rs b/nac3core/src/codegen/irrt/slice.rs index eb7037ac55..35e2151356 100644 --- a/nac3core/src/codegen/irrt/slice.rs +++ b/nac3core/src/codegen/irrt/slice.rs @@ -1,8 +1,6 @@ -use inkwell::{ - values::{BasicValueEnum, CallSiteValue, IntValue}, - IntPredicate, -}; +use inkwell::values::{BasicValueEnum, CallSiteValue, IntValue}; use itertools::Either; + use nac3parser::ast::Expr; use crate::{ @@ -39,38 +37,3 @@ pub fn handle_slice_index_bound<'ctx, G: CodeGenerator>( .unwrap(), )) } - -pub fn calculate_len_for_slice_range<'ctx, G: CodeGenerator + ?Sized>( - generator: &mut G, - ctx: &mut CodeGenContext<'ctx, '_>, - start: IntValue<'ctx>, - end: IntValue<'ctx>, - step: IntValue<'ctx>, -) -> IntValue<'ctx> { - const SYMBOL: &str = "__nac3_range_slice_len"; - let len_func = ctx.module.get_function(SYMBOL).unwrap_or_else(|| { - let i32_t = ctx.ctx.i32_type(); - let fn_t = i32_t.fn_type(&[i32_t.into(), i32_t.into(), i32_t.into()], false); - ctx.module.add_function(SYMBOL, fn_t, None) - }); - - // assert step != 0, throw exception if not - let not_zero = ctx - .builder - .build_int_compare(IntPredicate::NE, step, step.get_type().const_zero(), "range_step_ne") - .unwrap(); - ctx.make_assert( - generator, - not_zero, - "0:ValueError", - "step must not be zero", - [None, None, None], - ctx.current_loc, - ); - ctx.builder - .build_call(len_func, &[start.into(), end.into(), step.into()], "calc_len") - .map(CallSiteValue::try_as_basic_value) - .map(|v| v.map_left(BasicValueEnum::into_int_value)) - .map(Either::unwrap_left) - .unwrap() -} diff --git a/nac3core/src/codegen/types/mod.rs b/nac3core/src/codegen/types/mod.rs index 06baaa7b5d..022f897b33 100644 --- a/nac3core/src/codegen/types/mod.rs +++ b/nac3core/src/codegen/types/mod.rs @@ -29,6 +29,7 @@ mod list; pub mod ndarray; mod range; pub mod structure; +pub mod utils; /// A LLVM type that is used to represent a corresponding type in NAC3. pub trait ProxyType<'ctx>: Into { diff --git a/nac3core/src/codegen/types/utils/mod.rs b/nac3core/src/codegen/types/utils/mod.rs new file mode 100644 index 0000000000..d4137768c6 --- /dev/null +++ b/nac3core/src/codegen/types/utils/mod.rs @@ -0,0 +1,3 @@ +pub use slice::*; + +mod slice; diff --git a/nac3core/src/codegen/types/utils/slice.rs b/nac3core/src/codegen/types/utils/slice.rs new file mode 100644 index 0000000000..aba0efb217 --- /dev/null +++ b/nac3core/src/codegen/types/utils/slice.rs @@ -0,0 +1,254 @@ +use inkwell::{ + context::{AsContextRef, Context, ContextRef}, + types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType}, + values::IntValue, + AddressSpace, +}; +use itertools::Itertools; + +use nac3core_derive::StructFields; + +use crate::codegen::{ + types::{ + structure::{ + check_struct_type_matches_fields, FieldIndexCounter, StructField, StructFields, + }, + ProxyType, + }, + values::{utils::SliceValue, ArraySliceValue, ProxyValue}, + CodeGenContext, CodeGenerator, +}; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct SliceType<'ctx> { + ty: PointerType<'ctx>, + int_ty: IntType<'ctx>, + llvm_usize: IntType<'ctx>, +} + +#[derive(PartialEq, Eq, Clone, Copy, StructFields)] +pub struct SliceFields<'ctx> { + #[value_type(bool_type())] + pub start_defined: StructField<'ctx, IntValue<'ctx>>, + #[value_type(usize)] + pub start: StructField<'ctx, IntValue<'ctx>>, + #[value_type(bool_type())] + pub stop_defined: StructField<'ctx, IntValue<'ctx>>, + #[value_type(usize)] + pub stop: StructField<'ctx, IntValue<'ctx>>, + #[value_type(bool_type())] + pub step_defined: StructField<'ctx, IntValue<'ctx>>, + #[value_type(usize)] + pub step: StructField<'ctx, IntValue<'ctx>>, +} + +impl<'ctx> SliceFields<'ctx> { + /// Creates a new instance of [`SliceFields`] with a custom integer type for its range values. + #[must_use] + pub fn new_sized(ctx: &impl AsContextRef<'ctx>, int_ty: IntType<'ctx>) -> Self { + let ctx = unsafe { ContextRef::new(ctx.as_ctx_ref()) }; + let mut counter = FieldIndexCounter::default(); + + SliceFields { + start_defined: StructField::create(&mut counter, "start_defined", ctx.bool_type()), + start: StructField::create(&mut counter, "start", int_ty), + stop_defined: StructField::create(&mut counter, "stop_defined", ctx.bool_type()), + stop: StructField::create(&mut counter, "stop", int_ty), + step_defined: StructField::create(&mut counter, "step_defined", ctx.bool_type()), + step: StructField::create(&mut counter, "step", int_ty), + } + } +} + +impl<'ctx> SliceType<'ctx> { + /// Checks whether `llvm_ty` represents a `slice` type, returning [Err] if it does not. + pub fn is_representable( + llvm_ty: PointerType<'ctx>, + llvm_usize: IntType<'ctx>, + ) -> Result<(), String> { + let ctx = llvm_ty.get_context(); + + let fields = SliceFields::new(ctx, llvm_usize); + + let llvm_ty = llvm_ty.get_element_type(); + let AnyTypeEnum::StructType(llvm_ty) = llvm_ty else { + return Err(format!("Expected struct type for `Slice` type, got {llvm_ty}")); + }; + + check_struct_type_matches_fields( + fields, + llvm_ty, + "Slice", + &[ + (fields.start.name(), &|ty| { + if ty.is_int_type() { + Ok(()) + } else { + Err(format!("Expected int type for `Slice.start`, got {ty}")) + } + }), + (fields.stop.name(), &|ty| { + if ty.is_int_type() { + Ok(()) + } else { + Err(format!("Expected int type for `Slice.stop`, got {ty}")) + } + }), + (fields.step.name(), &|ty| { + if ty.is_int_type() { + Ok(()) + } else { + Err(format!("Expected int type for `Slice.step`, got {ty}")) + } + }), + ], + ) + } + + // TODO: Move this into e.g. StructProxyType + #[must_use] + pub fn get_fields(&self) -> SliceFields<'ctx> { + SliceFields::new_sized(&self.int_ty.get_context(), self.int_ty) + } + + /// Creates an LLVM type corresponding to the expected structure of a `Slice`. + #[must_use] + fn llvm_type(ctx: &'ctx Context, int_ty: IntType<'ctx>) -> PointerType<'ctx> { + let field_tys = SliceFields::new_sized(&int_ty.get_context(), int_ty) + .into_iter() + .map(|field| field.1) + .collect_vec(); + + ctx.struct_type(&field_tys, false).ptr_type(AddressSpace::default()) + } + + /// Creates an instance of [`SliceType`] with `int_ty` as its backing integer type. + #[must_use] + pub fn new(ctx: &'ctx Context, int_ty: IntType<'ctx>, llvm_usize: IntType<'ctx>) -> Self { + let llvm_ty = Self::llvm_type(ctx, int_ty); + + Self { ty: llvm_ty, int_ty, llvm_usize } + } + + /// Creates an instance of [`SliceType`] with `usize` as its backing integer type. + #[must_use] + pub fn new_usize(generator: &G, ctx: &'ctx Context) -> Self { + let llvm_usize = generator.get_size_type(ctx); + Self::new(ctx, llvm_usize, llvm_usize) + } + + /// Creates an [`SliceType`] from a [`PointerType`] representing a `slice`. + #[must_use] + pub fn from_type( + ptr_ty: PointerType<'ctx>, + int_ty: IntType<'ctx>, + llvm_usize: IntType<'ctx>, + ) -> Self { + debug_assert!(Self::is_representable(ptr_ty, int_ty).is_ok()); + + Self { ty: ptr_ty, int_ty, llvm_usize } + } + + #[must_use] + pub fn element_type(&self) -> IntType<'ctx> { + self.int_ty + } + + /// Allocates an instance of [`ContiguousNDArrayValue`] as if by calling `alloca` on the base type. + #[must_use] + pub fn alloca( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + name: Option<&'ctx str>, + ) -> >::Value { + >::Value::from_pointer_value( + self.raw_alloca(generator, ctx, name), + self.int_ty, + self.llvm_usize, + name, + ) + } + + /// Converts an existing value into a [`ContiguousNDArrayValue`]. + #[must_use] + pub fn map_value( + &self, + value: <>::Value as ProxyValue<'ctx>>::Base, + name: Option<&'ctx str>, + ) -> >::Value { + >::Value::from_pointer_value( + value, + self.int_ty, + self.llvm_usize, + name, + ) + } +} + +impl<'ctx> ProxyType<'ctx> for SliceType<'ctx> { + type Base = PointerType<'ctx>; + type Value = SliceValue<'ctx>; + + fn is_type( + generator: &G, + ctx: &'ctx Context, + llvm_ty: impl BasicType<'ctx>, + ) -> Result<(), String> { + if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() { + >::is_representable(generator, ctx, ty) + } else { + Err(format!("Expected pointer type, got {llvm_ty:?}")) + } + } + + fn is_representable( + generator: &G, + ctx: &'ctx Context, + llvm_ty: Self::Base, + ) -> Result<(), String> { + Self::is_representable(llvm_ty, generator.get_size_type(ctx)) + } + + fn raw_alloca( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + name: Option<&'ctx str>, + ) -> >::Base { + generator + .gen_var_alloc( + ctx, + self.as_base_type().get_element_type().into_struct_type().into(), + name, + ) + .unwrap() + } + + fn array_alloca( + &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().get_element_type().into_struct_type().into(), + size, + name, + ) + .unwrap() + } + + fn as_base_type(&self) -> Self::Base { + self.ty + } +} + +impl<'ctx> From> for PointerType<'ctx> { + fn from(value: SliceType<'ctx>) -> Self { + value.as_base_type() + } +} diff --git a/nac3core/src/codegen/values/mod.rs b/nac3core/src/codegen/values/mod.rs index b8bba4e6b1..032f041750 100644 --- a/nac3core/src/codegen/values/mod.rs +++ b/nac3core/src/codegen/values/mod.rs @@ -10,6 +10,7 @@ mod array; mod list; pub mod ndarray; mod range; +pub mod utils; /// A LLVM type that is used to represent a non-primitive value in NAC3. pub trait ProxyValue<'ctx>: Into { diff --git a/nac3core/src/codegen/values/utils/mod.rs b/nac3core/src/codegen/values/utils/mod.rs new file mode 100644 index 0000000000..d4137768c6 --- /dev/null +++ b/nac3core/src/codegen/values/utils/mod.rs @@ -0,0 +1,3 @@ +pub use slice::*; + +mod slice; diff --git a/nac3core/src/codegen/values/utils/slice.rs b/nac3core/src/codegen/values/utils/slice.rs new file mode 100644 index 0000000000..dffe6cefce --- /dev/null +++ b/nac3core/src/codegen/values/utils/slice.rs @@ -0,0 +1,231 @@ +use inkwell::{ + types::IntType, + values::{IntValue, PointerValue}, +}; + +use nac3parser::ast::Expr; + +use crate::{ + codegen::{ + types::{structure::StructField, utils::SliceType}, + values::ProxyValue, + CodeGenContext, CodeGenerator, + }, + typecheck::typedef::Type, +}; + +/// An IRRT representation of an (unresolved) slice. +#[derive(Copy, Clone)] +pub struct SliceValue<'ctx> { + value: PointerValue<'ctx>, + int_ty: IntType<'ctx>, + llvm_usize: IntType<'ctx>, + name: Option<&'ctx str>, +} + +impl<'ctx> SliceValue<'ctx> { + /// Checks whether `value` is an instance of `ContiguousNDArray`, returning [Err] if `value` is + /// not an instance. + pub fn is_representable( + value: PointerValue<'ctx>, + llvm_usize: IntType<'ctx>, + ) -> Result<(), String> { + >::Type::is_representable(value.get_type(), llvm_usize) + } + + /// Creates an [`SliceValue`] from a [`PointerValue`]. + #[must_use] + pub fn from_pointer_value( + ptr: PointerValue<'ctx>, + int_ty: IntType<'ctx>, + llvm_usize: IntType<'ctx>, + name: Option<&'ctx str>, + ) -> Self { + debug_assert!(Self::is_representable(ptr, llvm_usize).is_ok()); + + Self { value: ptr, int_ty, llvm_usize, name } + } + + fn start_defined_field(&self) -> StructField<'ctx, IntValue<'ctx>> { + self.get_type().get_fields().start_defined + } + + pub fn load_start_defined(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { + self.start_defined_field().get(ctx, self.value, self.name) + } + + fn start_field(&self) -> StructField<'ctx, IntValue<'ctx>> { + self.get_type().get_fields().start + } + + pub fn load_start(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { + self.start_field().get(ctx, self.value, self.name) + } + + pub fn store_start(&self, ctx: &CodeGenContext<'ctx, '_>, value: Option>) { + match value { + Some(start) => { + self.start_defined_field().set( + ctx, + self.value, + ctx.ctx.bool_type().const_all_ones(), + self.name, + ); + self.start_field().set(ctx, self.value, start, self.name); + } + + None => self.start_defined_field().set( + ctx, + self.value, + ctx.ctx.bool_type().const_zero(), + self.name, + ), + } + } + + fn stop_defined_field(&self) -> StructField<'ctx, IntValue<'ctx>> { + self.get_type().get_fields().stop_defined + } + + pub fn load_stop_defined(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { + self.stop_defined_field().get(ctx, self.value, self.name) + } + + fn stop_field(&self) -> StructField<'ctx, IntValue<'ctx>> { + self.get_type().get_fields().stop + } + + pub fn load_stop(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { + self.stop_field().get(ctx, self.value, self.name) + } + + pub fn store_stop(&self, ctx: &CodeGenContext<'ctx, '_>, value: Option>) { + match value { + Some(stop) => { + self.stop_defined_field().set( + ctx, + self.value, + ctx.ctx.bool_type().const_all_ones(), + self.name, + ); + self.stop_field().set(ctx, self.value, stop, self.name); + } + + None => self.stop_defined_field().set( + ctx, + self.value, + ctx.ctx.bool_type().const_zero(), + self.name, + ), + } + } + + fn step_defined_field(&self) -> StructField<'ctx, IntValue<'ctx>> { + self.get_type().get_fields().step_defined + } + + pub fn load_step_defined(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { + self.step_defined_field().get(ctx, self.value, self.name) + } + + fn step_field(&self) -> StructField<'ctx, IntValue<'ctx>> { + self.get_type().get_fields().step + } + + pub fn load_step(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { + self.step_field().get(ctx, self.value, self.name) + } + + pub fn store_step(&self, ctx: &CodeGenContext<'ctx, '_>, value: Option>) { + match value { + Some(step) => { + self.step_defined_field().set( + ctx, + self.value, + ctx.ctx.bool_type().const_all_ones(), + self.name, + ); + self.step_field().set(ctx, self.value, step, self.name); + } + + None => self.step_defined_field().set( + ctx, + self.value, + ctx.ctx.bool_type().const_zero(), + self.name, + ), + } + } +} + +impl<'ctx> ProxyValue<'ctx> for SliceValue<'ctx> { + type Base = PointerValue<'ctx>; + type Type = SliceType<'ctx>; + + fn get_type(&self) -> Self::Type { + Self::Type::from_type(self.value.get_type(), self.int_ty, self.llvm_usize) + } + + fn as_base_value(&self) -> Self::Base { + self.value + } +} + +impl<'ctx> From> for PointerValue<'ctx> { + fn from(value: SliceValue<'ctx>) -> Self { + value.as_base_value() + } +} + +/// A slice represented in compile-time by `start`, `stop` and `step`, all held as LLVM values. +// TODO: Rename this to CTConstSlice +#[derive(Debug, Copy, Clone)] +pub struct RustSlice<'ctx> { + int_ty: IntType<'ctx>, + start: Option>, + stop: Option>, + step: Option>, +} + +impl<'ctx> RustSlice<'ctx> { + /// Generate LLVM IR for an [`ExprKind::Slice`] and convert it into a [`RustSlice`]. + #[allow(clippy::type_complexity)] + pub fn from_slice_expr( + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + lower: &Option>>>, + upper: &Option>>>, + step: &Option>>>, + ) -> Result, String> { + let mut value_mapper = |value_expr: &Option>>>| -> Result<_, String> { + Ok(match value_expr { + None => None, + Some(value_expr) => { + let value_expr = generator + .gen_expr(ctx, value_expr)? + .map(|value| { + value.to_basic_value_enum(ctx, generator, ctx.primitives.int32) + }) + .unwrap()?; + + Some(value_expr.into_int_value()) + } + }) + }; + + let start = value_mapper(lower)?; + let stop = value_mapper(upper)?; + let step = value_mapper(step)?; + + Ok(RustSlice { int_ty: ctx.ctx.i32_type(), start, stop, step }) + } + + /// Write the contents to an LLVM [`SliceValue`]. + pub fn write_to_slice(&self, ctx: &CodeGenContext<'ctx, '_>, dst_slice_ptr: SliceValue<'ctx>) { + assert_eq!(self.int_ty, dst_slice_ptr.int_ty); + + dst_slice_ptr.store_start(ctx, self.start); + dst_slice_ptr.store_stop(ctx, self.stop); + dst_slice_ptr.store_step(ctx, self.step); + } +}