forked from M-Labs/nac3
[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).
This commit is contained in:
parent
8b3429d62a
commit
fdfc80ca5f
@ -2,6 +2,7 @@
|
|||||||
#include "irrt/list.hpp"
|
#include "irrt/list.hpp"
|
||||||
#include "irrt/math.hpp"
|
#include "irrt/math.hpp"
|
||||||
#include "irrt/ndarray.hpp"
|
#include "irrt/ndarray.hpp"
|
||||||
|
#include "irrt/range.hpp"
|
||||||
#include "irrt/slice.hpp"
|
#include "irrt/slice.hpp"
|
||||||
#include "irrt/ndarray/basic.hpp"
|
#include "irrt/ndarray/basic.hpp"
|
||||||
#include "irrt/ndarray/def.hpp"
|
#include "irrt/ndarray/def.hpp"
|
||||||
|
47
nac3core/irrt/irrt/range.hpp
Normal file
47
nac3core/irrt/irrt/range.hpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "irrt/debug.hpp"
|
||||||
|
#include "irrt/int_types.hpp"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
namespace range {
|
||||||
|
template<typename T>
|
||||||
|
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<typename T>
|
||||||
|
struct Range {
|
||||||
|
T start;
|
||||||
|
T stop;
|
||||||
|
T step;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculate the `len()` of this range.
|
||||||
|
*/
|
||||||
|
template<typename SizeT>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,145 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "irrt/debug.hpp"
|
||||||
|
#include "irrt/exception.hpp"
|
||||||
#include "irrt/int_types.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<typename T>
|
||||||
|
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<typename T>
|
||||||
|
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<typename T>
|
||||||
|
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<typename SizeT>
|
||||||
|
Range<T> indices(T length) {
|
||||||
|
// Reference:
|
||||||
|
// https://github.com/python/cpython/blob/main/Objects/sliceobject.c#L388
|
||||||
|
debug_assert(SizeT, length >= 0);
|
||||||
|
|
||||||
|
Range<T> 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<typename SizeT>
|
||||||
|
Range<T> 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<SizeT>(length);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
SliceIndex __nac3_slice_index_bound(SliceIndex i, const SliceIndex len) {
|
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;
|
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
|
|
@ -13,11 +13,13 @@ use super::{CodeGenContext, CodeGenerator};
|
|||||||
use crate::{symbol_resolver::SymbolResolver, typecheck::typedef::Type};
|
use crate::{symbol_resolver::SymbolResolver, typecheck::typedef::Type};
|
||||||
pub use list::*;
|
pub use list::*;
|
||||||
pub use math::*;
|
pub use math::*;
|
||||||
|
pub use range::*;
|
||||||
pub use slice::*;
|
pub use slice::*;
|
||||||
|
|
||||||
mod list;
|
mod list;
|
||||||
mod math;
|
mod math;
|
||||||
pub mod ndarray;
|
pub mod ndarray;
|
||||||
|
mod range;
|
||||||
mod slice;
|
mod slice;
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
42
nac3core/src/codegen/irrt/range.rs
Normal file
42
nac3core/src/codegen/irrt/range.rs
Normal file
@ -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()
|
||||||
|
}
|
@ -1,8 +1,6 @@
|
|||||||
use inkwell::{
|
use inkwell::values::{BasicValueEnum, CallSiteValue, IntValue};
|
||||||
values::{BasicValueEnum, CallSiteValue, IntValue},
|
|
||||||
IntPredicate,
|
|
||||||
};
|
|
||||||
use itertools::Either;
|
use itertools::Either;
|
||||||
|
|
||||||
use nac3parser::ast::Expr;
|
use nac3parser::ast::Expr;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -39,38 +37,3 @@ pub fn handle_slice_index_bound<'ctx, G: CodeGenerator>(
|
|||||||
.unwrap(),
|
.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()
|
|
||||||
}
|
|
||||||
|
@ -29,6 +29,7 @@ mod list;
|
|||||||
pub mod ndarray;
|
pub mod ndarray;
|
||||||
mod range;
|
mod range;
|
||||||
pub mod structure;
|
pub mod structure;
|
||||||
|
pub mod utils;
|
||||||
|
|
||||||
/// A LLVM type that is used to represent a corresponding type in NAC3.
|
/// A LLVM type that is used to represent a corresponding type in NAC3.
|
||||||
pub trait ProxyType<'ctx>: Into<Self::Base> {
|
pub trait ProxyType<'ctx>: Into<Self::Base> {
|
||||||
|
3
nac3core/src/codegen/types/utils/mod.rs
Normal file
3
nac3core/src/codegen/types/utils/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub use slice::*;
|
||||||
|
|
||||||
|
mod slice;
|
254
nac3core/src/codegen/types/utils/slice.rs
Normal file
254
nac3core/src/codegen/types/utils/slice.rs
Normal file
@ -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<G: CodeGenerator + ?Sized>(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<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
name: Option<&'ctx str>,
|
||||||
|
) -> <Self as ProxyType<'ctx>>::Value {
|
||||||
|
<Self as ProxyType<'ctx>>::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: <<Self as ProxyType<'ctx>>::Value as ProxyValue<'ctx>>::Base,
|
||||||
|
name: Option<&'ctx str>,
|
||||||
|
) -> <Self as ProxyType<'ctx>>::Value {
|
||||||
|
<Self as ProxyType<'ctx>>::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<G: CodeGenerator + ?Sized>(
|
||||||
|
generator: &G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
llvm_ty: impl BasicType<'ctx>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() {
|
||||||
|
<Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
|
||||||
|
} else {
|
||||||
|
Err(format!("Expected pointer 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, generator.get_size_type(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn raw_alloca<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
name: Option<&'ctx str>,
|
||||||
|
) -> <Self::Value as ProxyValue<'ctx>>::Base {
|
||||||
|
generator
|
||||||
|
.gen_var_alloc(
|
||||||
|
ctx,
|
||||||
|
self.as_base_type().get_element_type().into_struct_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().get_element_type().into_struct_type().into(),
|
||||||
|
size,
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_base_type(&self) -> Self::Base {
|
||||||
|
self.ty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx> From<SliceType<'ctx>> for PointerType<'ctx> {
|
||||||
|
fn from(value: SliceType<'ctx>) -> Self {
|
||||||
|
value.as_base_type()
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,7 @@ mod array;
|
|||||||
mod list;
|
mod list;
|
||||||
pub mod ndarray;
|
pub mod ndarray;
|
||||||
mod range;
|
mod range;
|
||||||
|
pub mod utils;
|
||||||
|
|
||||||
/// A LLVM type that is used to represent a non-primitive value in NAC3.
|
/// A LLVM type that is used to represent a non-primitive value in NAC3.
|
||||||
pub trait ProxyValue<'ctx>: Into<Self::Base> {
|
pub trait ProxyValue<'ctx>: Into<Self::Base> {
|
||||||
|
3
nac3core/src/codegen/values/utils/mod.rs
Normal file
3
nac3core/src/codegen/values/utils/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub use slice::*;
|
||||||
|
|
||||||
|
mod slice;
|
231
nac3core/src/codegen/values/utils/slice.rs
Normal file
231
nac3core/src/codegen/values/utils/slice.rs
Normal file
@ -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> {
|
||||||
|
<Self as ProxyValue<'ctx>>::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<IntValue<'ctx>>) {
|
||||||
|
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<IntValue<'ctx>>) {
|
||||||
|
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<IntValue<'ctx>>) {
|
||||||
|
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<SliceValue<'ctx>> 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<IntValue<'ctx>>,
|
||||||
|
stop: Option<IntValue<'ctx>>,
|
||||||
|
step: Option<IntValue<'ctx>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<G: CodeGenerator>(
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
lower: &Option<Box<Expr<Option<Type>>>>,
|
||||||
|
upper: &Option<Box<Expr<Option<Type>>>>,
|
||||||
|
step: &Option<Box<Expr<Option<Type>>>>,
|
||||||
|
) -> Result<RustSlice<'ctx>, String> {
|
||||||
|
let mut value_mapper = |value_expr: &Option<Box<Expr<Option<Type>>>>| -> 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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user