core/model: introduce models
This commit is contained in:
parent
d75c5d460f
commit
ff84c9bfaf
|
@ -41,6 +41,7 @@ pub mod extern_fns;
|
||||||
mod generator;
|
mod generator;
|
||||||
pub mod irrt;
|
pub mod irrt;
|
||||||
pub mod llvm_intrinsics;
|
pub mod llvm_intrinsics;
|
||||||
|
pub mod model;
|
||||||
pub mod numpy;
|
pub mod numpy;
|
||||||
pub mod stmt;
|
pub mod stmt;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
use inkwell::{
|
||||||
|
context::Context,
|
||||||
|
types::{BasicType, BasicTypeEnum},
|
||||||
|
values::BasicValueEnum,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::codegen::CodeGenerator;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// A [`Model`] of any [`BasicTypeEnum`].
|
||||||
|
///
|
||||||
|
/// Use this when you don't need/cannot have any static types to escape from the [`Model`] abstraction.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Any<'ctx>(pub BasicTypeEnum<'ctx>);
|
||||||
|
|
||||||
|
impl<'ctx> Model<'ctx> for Any<'ctx> {
|
||||||
|
type Value = BasicValueEnum<'ctx>;
|
||||||
|
type Type = BasicTypeEnum<'ctx>;
|
||||||
|
|
||||||
|
fn get_type<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
_generator: &G,
|
||||||
|
_ctx: &'ctx Context,
|
||||||
|
) -> Self::Type {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_type<T: BasicType<'ctx>, G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
_generator: &mut G,
|
||||||
|
_ctx: &'ctx Context,
|
||||||
|
ty: T,
|
||||||
|
) -> Result<(), ModelError> {
|
||||||
|
let ty = ty.as_basic_type_enum();
|
||||||
|
if ty == self.0 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(ModelError(format!("Expecting {}, but got {}", self.0, ty)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,141 @@
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use inkwell::{
|
||||||
|
context::Context,
|
||||||
|
types::{ArrayType, BasicType, BasicTypeEnum},
|
||||||
|
values::{ArrayValue, IntValue},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::codegen::{CodeGenContext, CodeGenerator};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Traits for a Rust struct that describes a length value for [`Array`].
|
||||||
|
pub trait LenKind: fmt::Debug + Clone + Copy {
|
||||||
|
fn get_length(&self) -> u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A statically known length.
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct Len<const N: u32>;
|
||||||
|
|
||||||
|
/// A dynamically known length.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct AnyLen(pub u32);
|
||||||
|
|
||||||
|
impl<const N: u32> LenKind for Len<N> {
|
||||||
|
fn get_length(&self) -> u32 {
|
||||||
|
N
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LenKind for AnyLen {
|
||||||
|
fn get_length(&self) -> u32 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Model for an [`ArrayType`].
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct Array<Len, Item> {
|
||||||
|
/// Length of this array.
|
||||||
|
pub len: Len,
|
||||||
|
/// [`Model`] of an array item.
|
||||||
|
pub item: Item,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx, Len: LenKind, Item: Model<'ctx>> Model<'ctx> for Array<Len, Item> {
|
||||||
|
type Value = ArrayValue<'ctx>;
|
||||||
|
type Type = ArrayType<'ctx>;
|
||||||
|
|
||||||
|
fn get_type<G: CodeGenerator + ?Sized>(&self, generator: &G, ctx: &'ctx Context) -> Self::Type {
|
||||||
|
self.item.get_type(generator, ctx).array_type(self.len.get_length())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_type<T: BasicType<'ctx>, G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
ty: T,
|
||||||
|
) -> Result<(), ModelError> {
|
||||||
|
let ty = ty.as_basic_type_enum();
|
||||||
|
let BasicTypeEnum::ArrayType(ty) = ty else {
|
||||||
|
return Err(ModelError(format!("Expecting ArrayType, but got {ty:?}")));
|
||||||
|
};
|
||||||
|
|
||||||
|
if ty.len() != self.len.get_length() {
|
||||||
|
return Err(ModelError(format!(
|
||||||
|
"Expecting ArrayType with size {}, but got an ArrayType with size {}",
|
||||||
|
ty.len(),
|
||||||
|
self.len.get_length()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.item
|
||||||
|
.check_type(generator, ctx, ty.get_element_type())
|
||||||
|
.map_err(|err| err.under_context("an ArrayType"))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx, Len: LenKind, Item: Model<'ctx>> Instance<'ctx, Ptr<Array<Len, Item>>> {
|
||||||
|
/// Get the pointer to the `i`-th (0-based) array element.
|
||||||
|
pub fn gep(
|
||||||
|
&self,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
i: IntValue<'ctx>,
|
||||||
|
) -> Instance<'ctx, Ptr<Item>> {
|
||||||
|
let zero = ctx.ctx.i32_type().const_zero();
|
||||||
|
let ptr = unsafe { ctx.builder.build_in_bounds_gep(self.value, &[zero, i], "").unwrap() };
|
||||||
|
|
||||||
|
Ptr(self.model.0.item).believe_value(ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Like `gep` but `i` is a constant.
|
||||||
|
pub fn gep_const(&self, ctx: &CodeGenContext<'ctx, '_>, i: u64) -> Instance<'ctx, Ptr<Item>> {
|
||||||
|
assert!(
|
||||||
|
i < u64::from(self.model.0.len.get_length()),
|
||||||
|
"Index {i} is out of bounds. Array length = {}",
|
||||||
|
self.model.0.len.get_length()
|
||||||
|
);
|
||||||
|
|
||||||
|
let i = ctx.ctx.i32_type().const_int(i, false);
|
||||||
|
self.gep(ctx, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience function equivalent to `.gep(...).load(...)`.
|
||||||
|
pub fn get<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
i: IntValue<'ctx>,
|
||||||
|
) -> Instance<'ctx, Item> {
|
||||||
|
self.gep(ctx, i).load(generator, ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Like `get` but `i` is a constant.
|
||||||
|
pub fn get_const<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
i: u64,
|
||||||
|
) -> Instance<'ctx, Item> {
|
||||||
|
self.gep_const(ctx, i).load(generator, ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience function equivalent to `.gep(...).store(...)`.
|
||||||
|
pub fn set(
|
||||||
|
&self,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
i: IntValue<'ctx>,
|
||||||
|
value: Instance<'ctx, Item>,
|
||||||
|
) {
|
||||||
|
self.gep(ctx, i).store(ctx, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Like `set` but `i` is a constant.
|
||||||
|
pub fn set_const(&self, ctx: &CodeGenContext<'ctx, '_>, i: u64, value: Instance<'ctx, Item>) {
|
||||||
|
self.gep_const(ctx, i).store(ctx, value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,212 @@
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use inkwell::{context::Context, types::*, values::*};
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::codegen::{CodeGenContext, CodeGenerator};
|
||||||
|
|
||||||
|
/// A error type for reporting any [`Model`]-related error (e.g., a [`BasicType`] mismatch).
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ModelError(pub String);
|
||||||
|
|
||||||
|
impl ModelError {
|
||||||
|
// Append a context message to the error.
|
||||||
|
pub(super) fn under_context(mut self, context: &str) -> Self {
|
||||||
|
self.0.push_str(" ... in ");
|
||||||
|
self.0.push_str(context);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait for Rust structs identifying [`BasicType`]s in the context of a known [`CodeGenerator`] and [`CodeGenContext`].
|
||||||
|
///
|
||||||
|
/// For instance,
|
||||||
|
/// - [`Int<Int32>`] identifies an [`IntType`] with 32-bits.
|
||||||
|
/// - [`Int<SizeT>`] identifies an [`IntType`] with bit-width [`CodeGenerator::get_size_type`].
|
||||||
|
/// - [`Ptr<Int<SizeT>>`] identifies a [`PointerType`] that points to an [`IntType`] with bit-width [`CodeGenerator::get_size_type`].
|
||||||
|
/// - [`Int<AnyInt>`] identifies an [`IntType`] with bit-width of whatever is set in the [`AnyInt`] object.
|
||||||
|
/// - [`Any`] identifies a [`BasicType`] set in the [`Any`] object itself.
|
||||||
|
///
|
||||||
|
/// You can get the [`BasicType`] out of a model with [`Model::get_type`].
|
||||||
|
///
|
||||||
|
/// Furthermore, [`Instance<'ctx, M>`] is a simple structure that carries a [`BasicValue`] with a [`BasicType`] identified by model `M`.
|
||||||
|
///
|
||||||
|
/// The main purpose of this abstraction is to have a more Rust type-safe way to use Inkwell and give type-hints
|
||||||
|
/// for programmers.
|
||||||
|
///
|
||||||
|
/// ### Notes on `Default` trait
|
||||||
|
///
|
||||||
|
/// For some models like [`Int<Int32>`] or [`Int<SizeT>`], they have a [`Default`] trait since just by looking at the type, it is possible
|
||||||
|
/// to tell which [`BasicType`] they are identifying.
|
||||||
|
///
|
||||||
|
/// This can be used to create strongly-typed interfaces accepting only values of a specific [`BasicType`] without having to worry about
|
||||||
|
/// writing debug assertions to check if the programmer has passed in an [`IntValue`] with the wrong bit-width.
|
||||||
|
/// ```ignore
|
||||||
|
/// fn give_me_i32_and_get_a_size_t_back<'ctx>(i32: Instance<'ctx, Int<Int32>>) -> Instance<'ctx, Int<SizeT>> {
|
||||||
|
/// // code...
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ### Notes on converting between Inkwell and model.
|
||||||
|
///
|
||||||
|
/// Suppose you have an [`IntValue`], and you want to pass it into a function that takes a [`Instance<'ctx, Int<Int32>>`]. You can do use
|
||||||
|
/// [`Model::check_value`] or [`Model::believe_value`].
|
||||||
|
/// ```ignore
|
||||||
|
/// let my_value: IntValue<'ctx>;
|
||||||
|
///
|
||||||
|
/// let my_value = Int(Int32).check_value(my_value).unwrap(); // Panics if `my_value` is not 32-bit with a descriptive error message.
|
||||||
|
///
|
||||||
|
/// // or, if you are absolutely certain that `my_value` is 32-bit and doing extra checks is a waste of time:
|
||||||
|
/// let my_value = Int(Int32).believe_value(my_value);
|
||||||
|
/// ```
|
||||||
|
pub trait Model<'ctx>: fmt::Debug + Clone + Copy {
|
||||||
|
/// The [`BasicType`] *variant* this model is identifying.
|
||||||
|
///
|
||||||
|
/// For [`Int<Int32>`], [`Int<SizeT>`], [`Int<Any>`], etc, this is [`IntValue`];
|
||||||
|
///
|
||||||
|
/// For [`Ptr<???>`], etc, this is [`PointerValue`];
|
||||||
|
///
|
||||||
|
/// For [`Any`], this is just [`BasicValueEnum`];
|
||||||
|
///
|
||||||
|
/// and so on.
|
||||||
|
type Type: BasicType<'ctx>;
|
||||||
|
|
||||||
|
/// The [`BasicValue`] type of the [`BasicType`] of this model.
|
||||||
|
type Value: BasicValue<'ctx> + TryFrom<BasicValueEnum<'ctx>>;
|
||||||
|
|
||||||
|
/// Return the [`BasicType`] of this model.
|
||||||
|
#[must_use]
|
||||||
|
fn get_type<G: CodeGenerator + ?Sized>(&self, generator: &G, ctx: &'ctx Context) -> Self::Type;
|
||||||
|
|
||||||
|
/// Get the number of bytes of the [`BasicType`] of this model.
|
||||||
|
fn sizeof<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
) -> IntValue<'ctx> {
|
||||||
|
self.get_type(generator, ctx).size_of().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a [`BasicType`] matches the [`BasicType`] of this model.
|
||||||
|
fn check_type<T: BasicType<'ctx>, G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
ty: T,
|
||||||
|
) -> Result<(), ModelError>;
|
||||||
|
|
||||||
|
/// Create an instance from a value with [`Instance::model`] being this model.
|
||||||
|
///
|
||||||
|
/// Caller must make sure the type of `value` and the type of this `model` are equivalent.
|
||||||
|
#[must_use]
|
||||||
|
fn believe_value(&self, value: Self::Value) -> Instance<'ctx, Self> {
|
||||||
|
Instance { model: *self, value }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a [`BasicValue`]'s type is equivalent to the type of this model.
|
||||||
|
/// Wrap it into an [`Instance`] if it is.
|
||||||
|
fn check_value<V: BasicValue<'ctx>, G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
value: V,
|
||||||
|
) -> Result<Instance<'ctx, Self>, ModelError> {
|
||||||
|
let value = value.as_basic_value_enum();
|
||||||
|
self.check_type(generator, ctx, value.get_type())
|
||||||
|
.map_err(|err| err.under_context(format!("the value {value:?}").as_str()))?;
|
||||||
|
|
||||||
|
let Ok(value) = Self::Value::try_from(value) else {
|
||||||
|
unreachable!("check_type() has bad implementation")
|
||||||
|
};
|
||||||
|
Ok(self.believe_value(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate a value on the stack and return its pointer.
|
||||||
|
fn alloca<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
) -> Instance<'ctx, Ptr<Self>> {
|
||||||
|
let p = ctx.builder.build_alloca(self.get_type(generator, ctx.ctx), "").unwrap();
|
||||||
|
Ptr(*self).believe_value(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate an array on the stack and return its pointer.
|
||||||
|
fn array_alloca<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
len: IntValue<'ctx>,
|
||||||
|
) -> Instance<'ctx, Ptr<Self>> {
|
||||||
|
let p = ctx.builder.build_array_alloca(self.get_type(generator, ctx.ctx), len, "").unwrap();
|
||||||
|
Ptr(*self).believe_value(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn var_alloca<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
name: Option<&str>,
|
||||||
|
) -> Result<Instance<'ctx, Ptr<Self>>, String> {
|
||||||
|
let ty = self.get_type(generator, ctx.ctx).as_basic_type_enum();
|
||||||
|
let p = generator.gen_var_alloc(ctx, ty, name)?;
|
||||||
|
Ok(Ptr(*self).believe_value(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn array_var_alloca<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||||
|
len: IntValue<'ctx>,
|
||||||
|
name: Option<&'ctx str>,
|
||||||
|
) -> Result<Instance<'ctx, Ptr<Self>>, String> {
|
||||||
|
// TODO: Remove ArraySliceValue
|
||||||
|
let ty = self.get_type(generator, ctx.ctx).as_basic_type_enum();
|
||||||
|
let p = generator.gen_array_var_alloc(ctx, ty, len, name)?;
|
||||||
|
Ok(Ptr(*self).believe_value(PointerValue::from(p)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate a constant array.
|
||||||
|
fn const_array<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
values: &[Instance<'ctx, Self>],
|
||||||
|
) -> Instance<'ctx, Array<AnyLen, Self>> {
|
||||||
|
macro_rules! make {
|
||||||
|
($t:expr, $into_value:expr) => {
|
||||||
|
$t.const_array(
|
||||||
|
&values
|
||||||
|
.iter()
|
||||||
|
.map(|x| $into_value(x.value.as_basic_value_enum()))
|
||||||
|
.collect_vec(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = match self.get_type(generator, ctx).as_basic_type_enum() {
|
||||||
|
BasicTypeEnum::ArrayType(t) => make!(t, BasicValueEnum::into_array_value),
|
||||||
|
BasicTypeEnum::IntType(t) => make!(t, BasicValueEnum::into_int_value),
|
||||||
|
BasicTypeEnum::FloatType(t) => make!(t, BasicValueEnum::into_float_value),
|
||||||
|
BasicTypeEnum::PointerType(t) => make!(t, BasicValueEnum::into_pointer_value),
|
||||||
|
BasicTypeEnum::StructType(t) => make!(t, BasicValueEnum::into_struct_value),
|
||||||
|
BasicTypeEnum::VectorType(t) => make!(t, BasicValueEnum::into_vector_value),
|
||||||
|
};
|
||||||
|
|
||||||
|
Array { len: AnyLen(values.len() as u32), item: *self }
|
||||||
|
.check_value(generator, ctx, value)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Instance<'ctx, M: Model<'ctx>> {
|
||||||
|
/// The model of this instance.
|
||||||
|
pub model: M,
|
||||||
|
/// The value of this instance.
|
||||||
|
///
|
||||||
|
/// Caller must make sure the type of `value` and the type of this `model` are equivalent,
|
||||||
|
/// down to having the same [`IntType::get_bit_width`] in case of [`IntType`] for example.
|
||||||
|
pub value: M::Value,
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use inkwell::{context::Context, types::FloatType, values::FloatValue};
|
||||||
|
|
||||||
|
use crate::codegen::CodeGenerator;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub trait FloatKind<'ctx>: fmt::Debug + Clone + Copy {
|
||||||
|
fn get_float_type<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
) -> FloatType<'ctx>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct Float32;
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct Float64;
|
||||||
|
|
||||||
|
impl<'ctx> FloatKind<'ctx> for Float32 {
|
||||||
|
fn get_float_type<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
_generator: &G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
) -> FloatType<'ctx> {
|
||||||
|
ctx.f32_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx> FloatKind<'ctx> for Float64 {
|
||||||
|
fn get_float_type<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
_generator: &G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
) -> FloatType<'ctx> {
|
||||||
|
ctx.f64_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct AnyFloat<'ctx>(FloatType<'ctx>);
|
||||||
|
|
||||||
|
impl<'ctx> FloatKind<'ctx> for AnyFloat<'ctx> {
|
||||||
|
fn get_float_type<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
_generator: &G,
|
||||||
|
_ctx: &'ctx Context,
|
||||||
|
) -> FloatType<'ctx> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct Float<N>(pub N);
|
||||||
|
|
||||||
|
impl<'ctx, N: FloatKind<'ctx>> Model<'ctx> for Float<N> {
|
||||||
|
type Value = FloatValue<'ctx>;
|
||||||
|
type Type = FloatType<'ctx>;
|
||||||
|
|
||||||
|
fn get_type<G: CodeGenerator + ?Sized>(&self, generator: &G, ctx: &'ctx Context) -> Self::Type {
|
||||||
|
self.0.get_float_type(generator, ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_type<T: inkwell::types::BasicType<'ctx>, G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
ty: T,
|
||||||
|
) -> Result<(), ModelError> {
|
||||||
|
let ty = ty.as_basic_type_enum();
|
||||||
|
let Ok(ty) = FloatType::try_from(ty) else {
|
||||||
|
return Err(ModelError(format!("Expecting FloatType, but got {ty:?}")));
|
||||||
|
};
|
||||||
|
|
||||||
|
let exp_ty = self.0.get_float_type(generator, ctx);
|
||||||
|
|
||||||
|
// TODO: Inkwell does not have get_bit_width for FloatType?
|
||||||
|
if ty != exp_ty {
|
||||||
|
return Err(ModelError(format!("Expecting {exp_ty:?}, but got {ty:?}")));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
use inkwell::{
|
||||||
|
attributes::{Attribute, AttributeLoc},
|
||||||
|
types::{BasicMetadataTypeEnum, BasicType, FunctionType},
|
||||||
|
values::{AnyValue, BasicMetadataValueEnum, BasicValue, BasicValueEnum, CallSiteValue},
|
||||||
|
};
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
use crate::codegen::{CodeGenContext, CodeGenerator};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
struct Arg<'ctx> {
|
||||||
|
ty: BasicMetadataTypeEnum<'ctx>,
|
||||||
|
val: BasicMetadataValueEnum<'ctx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A convenience structure to construct & call an LLVM function.
|
||||||
|
pub struct CallFunction<'ctx, 'a, 'b, 'c, 'd, G: CodeGenerator + ?Sized> {
|
||||||
|
generator: &'d mut G,
|
||||||
|
ctx: &'b CodeGenContext<'ctx, 'a>,
|
||||||
|
/// Function name
|
||||||
|
name: &'c str,
|
||||||
|
/// Call arguments
|
||||||
|
args: Vec<Arg<'ctx>>,
|
||||||
|
/// LLVM function Attributes
|
||||||
|
attrs: Vec<&'static str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx, 'a, 'b, 'c, 'd, G: CodeGenerator + ?Sized> CallFunction<'ctx, 'a, 'b, 'c, 'd, G> {
|
||||||
|
pub fn begin(generator: &'d mut G, ctx: &'b CodeGenContext<'ctx, 'a>, name: &'c str) -> Self {
|
||||||
|
CallFunction { generator, ctx, name, args: Vec::new(), attrs: Vec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push a list of LLVM function attributes to the function declaration.
|
||||||
|
#[must_use]
|
||||||
|
pub fn attrs(mut self, attrs: Vec<&'static str>) -> Self {
|
||||||
|
self.attrs = attrs;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push a call argument to the function call.
|
||||||
|
#[allow(clippy::needless_pass_by_value)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn arg<M: Model<'ctx>>(mut self, arg: Instance<'ctx, M>) -> Self {
|
||||||
|
let arg = Arg {
|
||||||
|
ty: arg.model.get_type(self.generator, self.ctx.ctx).as_basic_type_enum().into(),
|
||||||
|
val: arg.value.as_basic_value_enum().into(),
|
||||||
|
};
|
||||||
|
self.args.push(arg);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call the function and expect the function to return a value of type of `return_model`.
|
||||||
|
#[must_use]
|
||||||
|
pub fn returning<M: Model<'ctx>>(self, name: &str, return_model: M) -> Instance<'ctx, M> {
|
||||||
|
let ret_ty = return_model.get_type(self.generator, self.ctx.ctx);
|
||||||
|
|
||||||
|
let ret = self.call(|tys| ret_ty.fn_type(tys, false), name);
|
||||||
|
let ret = BasicValueEnum::try_from(ret.as_any_value_enum()).unwrap(); // Must work
|
||||||
|
let ret = return_model.check_value(self.generator, self.ctx.ctx, ret).unwrap(); // Must work
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Like [`CallFunction::returning_`] but `return_model` is automatically inferred.
|
||||||
|
#[must_use]
|
||||||
|
pub fn returning_auto<M: Model<'ctx> + Default>(self, name: &str) -> Instance<'ctx, M> {
|
||||||
|
self.returning(name, M::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call the function and expect the function to return a void-type.
|
||||||
|
pub fn returning_void(self) {
|
||||||
|
let ret_ty = self.ctx.ctx.void_type();
|
||||||
|
|
||||||
|
let _ = self.call(|tys| ret_ty.fn_type(tys, false), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call<F>(&self, make_fn_type: F, return_value_name: &str) -> CallSiteValue<'ctx>
|
||||||
|
where
|
||||||
|
F: FnOnce(&[BasicMetadataTypeEnum<'ctx>]) -> FunctionType<'ctx>,
|
||||||
|
{
|
||||||
|
// Get the LLVM function.
|
||||||
|
let func = self.ctx.module.get_function(self.name).unwrap_or_else(|| {
|
||||||
|
// Declare the function if it doesn't exist.
|
||||||
|
let tys = self.args.iter().map(|arg| arg.ty).collect_vec();
|
||||||
|
|
||||||
|
let func_type = make_fn_type(&tys);
|
||||||
|
let func = self.ctx.module.add_function(self.name, func_type, None);
|
||||||
|
|
||||||
|
for attr in &self.attrs {
|
||||||
|
func.add_attribute(
|
||||||
|
AttributeLoc::Function,
|
||||||
|
self.ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
func
|
||||||
|
});
|
||||||
|
|
||||||
|
let vals = self.args.iter().map(|arg| arg.val).collect_vec();
|
||||||
|
self.ctx.builder.build_call(func, &vals, return_value_name).unwrap()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,417 @@
|
||||||
|
use std::{cmp::Ordering, fmt};
|
||||||
|
|
||||||
|
use inkwell::{
|
||||||
|
context::Context,
|
||||||
|
types::{BasicType, IntType},
|
||||||
|
values::IntValue,
|
||||||
|
IntPredicate,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::codegen::{CodeGenContext, CodeGenerator};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub trait IntKind<'ctx>: fmt::Debug + Clone + Copy {
|
||||||
|
fn get_int_type<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
) -> IntType<'ctx>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct Bool;
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct Byte;
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct Int32;
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct Int64;
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct SizeT;
|
||||||
|
|
||||||
|
impl<'ctx> IntKind<'ctx> for Bool {
|
||||||
|
fn get_int_type<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
_generator: &G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
) -> IntType<'ctx> {
|
||||||
|
ctx.bool_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx> IntKind<'ctx> for Byte {
|
||||||
|
fn get_int_type<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
_generator: &G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
) -> IntType<'ctx> {
|
||||||
|
ctx.i8_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx> IntKind<'ctx> for Int32 {
|
||||||
|
fn get_int_type<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
_generator: &G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
) -> IntType<'ctx> {
|
||||||
|
ctx.i32_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx> IntKind<'ctx> for Int64 {
|
||||||
|
fn get_int_type<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
_generator: &G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
) -> IntType<'ctx> {
|
||||||
|
ctx.i64_type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx> IntKind<'ctx> for SizeT {
|
||||||
|
fn get_int_type<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
) -> IntType<'ctx> {
|
||||||
|
generator.get_size_type(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct AnyInt<'ctx>(pub IntType<'ctx>);
|
||||||
|
|
||||||
|
impl<'ctx> IntKind<'ctx> for AnyInt<'ctx> {
|
||||||
|
fn get_int_type<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
_generator: &G,
|
||||||
|
_ctx: &'ctx Context,
|
||||||
|
) -> IntType<'ctx> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct Int<N>(pub N);
|
||||||
|
|
||||||
|
impl<'ctx, N: IntKind<'ctx>> Model<'ctx> for Int<N> {
|
||||||
|
type Value = IntValue<'ctx>;
|
||||||
|
type Type = IntType<'ctx>;
|
||||||
|
|
||||||
|
fn get_type<G: CodeGenerator + ?Sized>(&self, generator: &G, ctx: &'ctx Context) -> Self::Type {
|
||||||
|
self.0.get_int_type(generator, ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_type<T: BasicType<'ctx>, G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
ty: T,
|
||||||
|
) -> Result<(), ModelError> {
|
||||||
|
let ty = ty.as_basic_type_enum();
|
||||||
|
let Ok(ty) = IntType::try_from(ty) else {
|
||||||
|
return Err(ModelError(format!("Expecting IntType, but got {ty:?}")));
|
||||||
|
};
|
||||||
|
|
||||||
|
let exp_ty = self.0.get_int_type(generator, ctx);
|
||||||
|
if ty.get_bit_width() != exp_ty.get_bit_width() {
|
||||||
|
return Err(ModelError(format!(
|
||||||
|
"Expecting IntType to have {} bit(s), but got {} bit(s)",
|
||||||
|
exp_ty.get_bit_width(),
|
||||||
|
ty.get_bit_width()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx, N: IntKind<'ctx>> Int<N> {
|
||||||
|
pub fn const_int<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
value: u64,
|
||||||
|
) -> Instance<'ctx, Self> {
|
||||||
|
let value = self.get_type(generator, ctx).const_int(value, false);
|
||||||
|
self.believe_value(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn const_0<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
) -> Instance<'ctx, Self> {
|
||||||
|
let value = self.get_type(generator, ctx).const_zero();
|
||||||
|
self.believe_value(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn const_1<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
) -> Instance<'ctx, Self> {
|
||||||
|
self.const_int(generator, ctx, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn const_all_ones<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
) -> Instance<'ctx, Self> {
|
||||||
|
let value = self.get_type(generator, ctx).const_all_ones();
|
||||||
|
self.believe_value(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn s_extend_or_bit_cast<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
value: IntValue<'ctx>,
|
||||||
|
) -> Instance<'ctx, Self> {
|
||||||
|
assert!(
|
||||||
|
value.get_type().get_bit_width()
|
||||||
|
<= self.0.get_int_type(generator, ctx.ctx).get_bit_width()
|
||||||
|
);
|
||||||
|
let value = ctx
|
||||||
|
.builder
|
||||||
|
.build_int_s_extend_or_bit_cast(value, self.get_type(generator, ctx.ctx), "")
|
||||||
|
.unwrap();
|
||||||
|
self.believe_value(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn s_extend<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
value: IntValue<'ctx>,
|
||||||
|
) -> Instance<'ctx, Self> {
|
||||||
|
assert!(
|
||||||
|
value.get_type().get_bit_width()
|
||||||
|
< self.0.get_int_type(generator, ctx.ctx).get_bit_width()
|
||||||
|
);
|
||||||
|
let value =
|
||||||
|
ctx.builder.build_int_s_extend(value, self.get_type(generator, ctx.ctx), "").unwrap();
|
||||||
|
self.believe_value(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn z_extend_or_bit_cast<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
value: IntValue<'ctx>,
|
||||||
|
) -> Instance<'ctx, Self> {
|
||||||
|
assert!(
|
||||||
|
value.get_type().get_bit_width()
|
||||||
|
<= self.0.get_int_type(generator, ctx.ctx).get_bit_width()
|
||||||
|
);
|
||||||
|
let value = ctx
|
||||||
|
.builder
|
||||||
|
.build_int_z_extend_or_bit_cast(value, self.get_type(generator, ctx.ctx), "")
|
||||||
|
.unwrap();
|
||||||
|
self.believe_value(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn z_extend<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
value: IntValue<'ctx>,
|
||||||
|
) -> Instance<'ctx, Self> {
|
||||||
|
assert!(
|
||||||
|
value.get_type().get_bit_width()
|
||||||
|
< self.0.get_int_type(generator, ctx.ctx).get_bit_width()
|
||||||
|
);
|
||||||
|
let value =
|
||||||
|
ctx.builder.build_int_z_extend(value, self.get_type(generator, ctx.ctx), "").unwrap();
|
||||||
|
self.believe_value(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn truncate_or_bit_cast<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
value: IntValue<'ctx>,
|
||||||
|
) -> Instance<'ctx, Self> {
|
||||||
|
assert!(
|
||||||
|
value.get_type().get_bit_width()
|
||||||
|
>= self.0.get_int_type(generator, ctx.ctx).get_bit_width()
|
||||||
|
);
|
||||||
|
let value = ctx
|
||||||
|
.builder
|
||||||
|
.build_int_truncate_or_bit_cast(value, self.get_type(generator, ctx.ctx), "")
|
||||||
|
.unwrap();
|
||||||
|
self.believe_value(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn truncate<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
value: IntValue<'ctx>,
|
||||||
|
) -> Instance<'ctx, Self> {
|
||||||
|
assert!(
|
||||||
|
value.get_type().get_bit_width()
|
||||||
|
> self.0.get_int_type(generator, ctx.ctx).get_bit_width()
|
||||||
|
);
|
||||||
|
let value =
|
||||||
|
ctx.builder.build_int_truncate(value, self.get_type(generator, ctx.ctx), "").unwrap();
|
||||||
|
self.believe_value(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `sext` or `trunc` an int to this model's int type. Does nothing if equal bit-widths.
|
||||||
|
pub fn s_extend_or_truncate<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
value: IntValue<'ctx>,
|
||||||
|
) -> Instance<'ctx, Self> {
|
||||||
|
let their_width = value.get_type().get_bit_width();
|
||||||
|
let our_width = self.0.get_int_type(generator, ctx.ctx).get_bit_width();
|
||||||
|
match their_width.cmp(&our_width) {
|
||||||
|
Ordering::Less => self.s_extend(generator, ctx, value),
|
||||||
|
Ordering::Equal => self.believe_value(value),
|
||||||
|
Ordering::Greater => self.truncate(generator, ctx, value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `zext` or `trunc` an int to this model's int type. Does nothing if equal bit-widths.
|
||||||
|
pub fn z_extend_or_truncate<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
value: IntValue<'ctx>,
|
||||||
|
) -> Instance<'ctx, Self> {
|
||||||
|
let their_width = value.get_type().get_bit_width();
|
||||||
|
let our_width = self.0.get_int_type(generator, ctx.ctx).get_bit_width();
|
||||||
|
match their_width.cmp(&our_width) {
|
||||||
|
Ordering::Less => self.z_extend(generator, ctx, value),
|
||||||
|
Ordering::Equal => self.believe_value(value),
|
||||||
|
Ordering::Greater => self.truncate(generator, ctx, value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Int<Bool> {
|
||||||
|
#[must_use]
|
||||||
|
pub fn const_false<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
) -> Instance<'ctx, Self> {
|
||||||
|
self.const_int(generator, ctx, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn const_true<'ctx, G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
) -> Instance<'ctx, Self> {
|
||||||
|
self.const_int(generator, ctx, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx, N: IntKind<'ctx>> Instance<'ctx, Int<N>> {
|
||||||
|
pub fn s_extend_or_bit_cast<NewN: IntKind<'ctx>, G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
to_int_kind: NewN,
|
||||||
|
) -> Instance<'ctx, Int<NewN>> {
|
||||||
|
Int(to_int_kind).s_extend_or_bit_cast(generator, ctx, self.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn s_extend<NewN: IntKind<'ctx>, G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
to_int_kind: NewN,
|
||||||
|
) -> Instance<'ctx, Int<NewN>> {
|
||||||
|
Int(to_int_kind).s_extend(generator, ctx, self.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn z_extend_or_bit_cast<NewN: IntKind<'ctx>, G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
to_int_kind: NewN,
|
||||||
|
) -> Instance<'ctx, Int<NewN>> {
|
||||||
|
Int(to_int_kind).z_extend_or_bit_cast(generator, ctx, self.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn z_extend<NewN: IntKind<'ctx>, G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
to_int_kind: NewN,
|
||||||
|
) -> Instance<'ctx, Int<NewN>> {
|
||||||
|
Int(to_int_kind).z_extend(generator, ctx, self.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn truncate_or_bit_cast<NewN: IntKind<'ctx>, G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
to_int_kind: NewN,
|
||||||
|
) -> Instance<'ctx, Int<NewN>> {
|
||||||
|
Int(to_int_kind).truncate_or_bit_cast(generator, ctx, self.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn truncate<NewN: IntKind<'ctx>, G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
to_int_kind: NewN,
|
||||||
|
) -> Instance<'ctx, Int<NewN>> {
|
||||||
|
Int(to_int_kind).truncate(generator, ctx, self.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn s_extend_or_truncate<NewN: IntKind<'ctx>, G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
to_int_kind: NewN,
|
||||||
|
) -> Instance<'ctx, Int<NewN>> {
|
||||||
|
Int(to_int_kind).s_extend_or_truncate(generator, ctx, self.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn z_extend_or_truncate<NewN: IntKind<'ctx>, G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
to_int_kind: NewN,
|
||||||
|
) -> Instance<'ctx, Int<NewN>> {
|
||||||
|
Int(to_int_kind).z_extend_or_truncate(generator, ctx, self.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn add(&self, ctx: &CodeGenContext<'ctx, '_>, other: Self) -> Self {
|
||||||
|
let value = ctx.builder.build_int_add(self.value, other.value, "").unwrap();
|
||||||
|
self.model.believe_value(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn sub(&self, ctx: &CodeGenContext<'ctx, '_>, other: Self) -> Self {
|
||||||
|
let value = ctx.builder.build_int_sub(self.value, other.value, "").unwrap();
|
||||||
|
self.model.believe_value(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn mul(&self, ctx: &CodeGenContext<'ctx, '_>, other: Self) -> Self {
|
||||||
|
let value = ctx.builder.build_int_mul(self.value, other.value, "").unwrap();
|
||||||
|
self.model.believe_value(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compare(
|
||||||
|
&self,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
op: IntPredicate,
|
||||||
|
other: Self,
|
||||||
|
) -> Instance<'ctx, Int<Bool>> {
|
||||||
|
let value = ctx.builder.build_int_compare(op, self.value, other.value, "").unwrap();
|
||||||
|
Int(Bool).believe_value(value)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
mod any;
|
||||||
|
mod array;
|
||||||
|
mod core;
|
||||||
|
mod float;
|
||||||
|
pub mod function;
|
||||||
|
mod int;
|
||||||
|
mod ptr;
|
||||||
|
mod structure;
|
||||||
|
|
||||||
|
pub use any::*;
|
||||||
|
pub use array::*;
|
||||||
|
pub use core::*;
|
||||||
|
pub use float::*;
|
||||||
|
pub use int::*;
|
||||||
|
pub use ptr::*;
|
||||||
|
pub use structure::*;
|
|
@ -0,0 +1,188 @@
|
||||||
|
use inkwell::{
|
||||||
|
context::Context,
|
||||||
|
types::{BasicType, BasicTypeEnum, PointerType},
|
||||||
|
values::{IntValue, PointerValue},
|
||||||
|
AddressSpace,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::codegen::{llvm_intrinsics::call_memcpy_generic, CodeGenContext, CodeGenerator};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct Ptr<Item>(pub Item);
|
||||||
|
|
||||||
|
impl<'ctx, Item: Model<'ctx>> Model<'ctx> for Ptr<Item> {
|
||||||
|
type Value = PointerValue<'ctx>;
|
||||||
|
type Type = PointerType<'ctx>;
|
||||||
|
|
||||||
|
fn get_type<G: CodeGenerator + ?Sized>(&self, generator: &G, ctx: &'ctx Context) -> Self::Type {
|
||||||
|
self.0.get_type(generator, ctx).ptr_type(AddressSpace::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_type<T: BasicType<'ctx>, G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
ty: T,
|
||||||
|
) -> Result<(), ModelError> {
|
||||||
|
let ty = ty.as_basic_type_enum();
|
||||||
|
let Ok(ty) = PointerType::try_from(ty) else {
|
||||||
|
return Err(ModelError(format!("Expecting PointerType, but got {ty:?}")));
|
||||||
|
};
|
||||||
|
|
||||||
|
let elem_ty = ty.get_element_type();
|
||||||
|
let Ok(elem_ty) = BasicTypeEnum::try_from(elem_ty) else {
|
||||||
|
return Err(ModelError(format!(
|
||||||
|
"Expecting pointer element type to be a BasicTypeEnum, but got {elem_ty:?}"
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: inkwell `get_element_type()` will be deprecated.
|
||||||
|
// Remove the check for `get_element_type()` when the time comes.
|
||||||
|
self.0
|
||||||
|
.check_type(generator, ctx, elem_ty)
|
||||||
|
.map_err(|err| err.under_context("a PointerType"))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx, Element: Model<'ctx>> Ptr<Element> {
|
||||||
|
/// Return a ***constant*** nullptr.
|
||||||
|
pub fn nullptr<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
) -> Instance<'ctx, Ptr<Element>> {
|
||||||
|
let ptr = self.get_type(generator, ctx).const_null();
|
||||||
|
self.believe_value(ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cast a pointer into this model with [`inkwell::builder::Builder::build_pointer_cast`]
|
||||||
|
pub fn pointer_cast<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
ptr: PointerValue<'ctx>,
|
||||||
|
) -> Instance<'ctx, Ptr<Element>> {
|
||||||
|
let t = self.get_type(generator, ctx.ctx);
|
||||||
|
let ptr = ctx.builder.build_pointer_cast(ptr, t, "").unwrap();
|
||||||
|
self.believe_value(ptr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx, Item: Model<'ctx>> Instance<'ctx, Ptr<Item>> {
|
||||||
|
/// Offset the pointer by [`inkwell::builder::Builder::build_in_bounds_gep`].
|
||||||
|
#[must_use]
|
||||||
|
pub fn offset(
|
||||||
|
&self,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
offset: IntValue<'ctx>,
|
||||||
|
) -> Instance<'ctx, Ptr<Item>> {
|
||||||
|
let p = unsafe { ctx.builder.build_in_bounds_gep(self.value, &[offset], "").unwrap() };
|
||||||
|
self.model.believe_value(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Offset the pointer by [`inkwell::builder::Builder::build_in_bounds_gep`] by a constant offset.
|
||||||
|
#[must_use]
|
||||||
|
pub fn offset_const(
|
||||||
|
&self,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
offset: u64,
|
||||||
|
) -> Instance<'ctx, Ptr<Item>> {
|
||||||
|
let offset = ctx.ctx.i32_type().const_int(offset, false);
|
||||||
|
self.offset(ctx, offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_index(
|
||||||
|
&self,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
index: IntValue<'ctx>,
|
||||||
|
value: Instance<'ctx, Item>,
|
||||||
|
) {
|
||||||
|
self.offset(ctx, index).store(ctx, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_index_const(
|
||||||
|
&self,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
index: u64,
|
||||||
|
value: Instance<'ctx, Item>,
|
||||||
|
) {
|
||||||
|
self.offset_const(ctx, index).store(ctx, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_index<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
index: IntValue<'ctx>,
|
||||||
|
) -> Instance<'ctx, Item> {
|
||||||
|
self.offset(ctx, index).load(generator, ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_index_const<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
index: u64,
|
||||||
|
) -> Instance<'ctx, Item> {
|
||||||
|
self.offset_const(ctx, index).load(generator, ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load the value with [`inkwell::builder::Builder::build_load`].
|
||||||
|
pub fn load<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
) -> Instance<'ctx, Item> {
|
||||||
|
let value = ctx.builder.build_load(self.value, "").unwrap();
|
||||||
|
self.model.0.check_value(generator, ctx.ctx, value).unwrap() // If unwrap() panics, there is a logic error.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Store a value with [`inkwell::builder::Builder::build_store`].
|
||||||
|
pub fn store(&self, ctx: &CodeGenContext<'ctx, '_>, value: Instance<'ctx, Item>) {
|
||||||
|
ctx.builder.build_store(self.value, value.value).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a casted pointer of element type `NewElement` with [`inkwell::builder::Builder::build_pointer_cast`].
|
||||||
|
pub fn pointer_cast<NewItem: Model<'ctx>, G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
new_item: NewItem,
|
||||||
|
) -> Instance<'ctx, Ptr<NewItem>> {
|
||||||
|
Ptr(new_item).pointer_cast(generator, ctx, self.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the pointer is null with [`inkwell::builder::Builder::build_is_null`].
|
||||||
|
pub fn is_null(&self, ctx: &CodeGenContext<'ctx, '_>) -> Instance<'ctx, Int<Bool>> {
|
||||||
|
let value = ctx.builder.build_is_null(self.value, "").unwrap();
|
||||||
|
Int(Bool).believe_value(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the pointer is not null with [`inkwell::builder::Builder::build_is_not_null`].
|
||||||
|
pub fn is_not_null(&self, ctx: &CodeGenContext<'ctx, '_>) -> Instance<'ctx, Int<Bool>> {
|
||||||
|
let value = ctx.builder.build_is_not_null(self.value, "").unwrap();
|
||||||
|
Int(Bool).believe_value(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `memcpy` from another pointer.
|
||||||
|
pub fn copy_from<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
source: Self,
|
||||||
|
num_items: IntValue<'ctx>,
|
||||||
|
) {
|
||||||
|
// Force extend `num_items` and `itemsize` to `i64` so their types would match.
|
||||||
|
let itemsize = self.model.sizeof(generator, ctx.ctx);
|
||||||
|
let itemsize = Int(Int64).z_extend_or_truncate(generator, ctx, itemsize);
|
||||||
|
let num_items = Int(Int64).z_extend_or_truncate(generator, ctx, num_items);
|
||||||
|
let totalsize = itemsize.mul(ctx, num_items);
|
||||||
|
|
||||||
|
let is_volatile = ctx.ctx.bool_type().const_zero(); // is_volatile = false
|
||||||
|
call_memcpy_generic(ctx, self.value, source.value, totalsize.value, is_volatile);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,345 @@
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use inkwell::{
|
||||||
|
context::Context,
|
||||||
|
types::{BasicType, BasicTypeEnum, StructType},
|
||||||
|
values::{BasicValueEnum, StructValue},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::codegen::{CodeGenContext, CodeGenerator};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// A traveral that traverses a Rust `struct` that is used to declare an LLVM's struct's field types.
|
||||||
|
pub trait FieldTraversal<'ctx> {
|
||||||
|
/// Output type of [`FieldTraversal::add`].
|
||||||
|
type Out<M>;
|
||||||
|
|
||||||
|
/// Traverse through the type of a declared field and do something with it.
|
||||||
|
///
|
||||||
|
/// * `name` - The cosmetic name of the LLVM field. Used for debugging.
|
||||||
|
/// * `model` - The [`Model`] representing the LLVM type of this field.
|
||||||
|
fn add<M: Model<'ctx>>(&mut self, name: &'static str, model: M) -> Self::Out<M>;
|
||||||
|
|
||||||
|
/// Like [`FieldTraversal::add`] but [`Model`] is automatically inferred from its [`Default`] trait.
|
||||||
|
fn add_auto<M: Model<'ctx> + Default>(&mut self, name: &'static str) -> Self::Out<M> {
|
||||||
|
self.add(name, M::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Descriptor of an LLVM struct field.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct GepField<M> {
|
||||||
|
/// The GEP index of this field. This is the index to use with `build_gep`.
|
||||||
|
pub gep_index: u64,
|
||||||
|
/// The cosmetic name of this field.
|
||||||
|
pub name: &'static str,
|
||||||
|
/// The [`Model`] of this field's type.
|
||||||
|
pub model: M,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A traversal to get the GEP index of fields.
|
||||||
|
pub struct GepFieldTraversal {
|
||||||
|
/// The current GEP index.
|
||||||
|
gep_index_counter: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx> FieldTraversal<'ctx> for GepFieldTraversal {
|
||||||
|
type Out<M> = GepField<M>;
|
||||||
|
|
||||||
|
fn add<M: Model<'ctx>>(&mut self, name: &'static str, model: M) -> Self::Out<M> {
|
||||||
|
let gep_index = self.gep_index_counter;
|
||||||
|
self.gep_index_counter += 1;
|
||||||
|
Self::Out { gep_index, name, model }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A traversal to collect the field types of a struct.
|
||||||
|
///
|
||||||
|
/// This is used to collect the field types for [`Context::struct_type`].
|
||||||
|
struct TypeFieldTraversal<'ctx, 'a, G: CodeGenerator + ?Sized> {
|
||||||
|
generator: &'a G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
/// The collected field types so far, in order.
|
||||||
|
field_types: Vec<BasicTypeEnum<'ctx>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx, 'a, G: CodeGenerator + ?Sized> FieldTraversal<'ctx> for TypeFieldTraversal<'ctx, 'a, G> {
|
||||||
|
type Out<M> = (); // Checking types return nothing.
|
||||||
|
|
||||||
|
fn add<M: Model<'ctx>>(&mut self, _name: &'static str, model: M) -> Self::Out<M> {
|
||||||
|
let t = model.get_type(self.generator, self.ctx).as_basic_type_enum();
|
||||||
|
self.field_types.push(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A traversal to check the types of a field for debug assertions.
|
||||||
|
struct CheckTypeFieldTraversal<'ctx, 'a, G: CodeGenerator + ?Sized> {
|
||||||
|
generator: &'a mut G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
/// The current GEP index, so we can tell the index of the field we are checking
|
||||||
|
/// and report the GEP index.
|
||||||
|
index: u32,
|
||||||
|
/// The [`StructType`] to check.
|
||||||
|
scrutinee: StructType<'ctx>,
|
||||||
|
/// A list of collected errors so far.
|
||||||
|
errors: Vec<ModelError>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx, 'a, G: CodeGenerator + ?Sized> FieldTraversal<'ctx>
|
||||||
|
for CheckTypeFieldTraversal<'ctx, 'a, G>
|
||||||
|
{
|
||||||
|
type Out<M> = (); // Checking types return nothing.
|
||||||
|
|
||||||
|
fn add<M: Model<'ctx>>(&mut self, name: &'static str, model: M) -> Self::Out<M> {
|
||||||
|
let i = self.index;
|
||||||
|
self.index += 1;
|
||||||
|
|
||||||
|
if let Some(t) = self.scrutinee.get_field_type_at_index(i) {
|
||||||
|
if let Err(err) = model.check_type(self.generator, self.ctx, t) {
|
||||||
|
self.errors.push(err.under_context(format!("field #{i} '{name}'").as_str()));
|
||||||
|
}
|
||||||
|
} // Otherwise, it will be caught by Struct's `check_type`.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait for Rust structs identifying LLVM structures.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
///
|
||||||
|
/// Suppose you want to define this structure:
|
||||||
|
/// ```c
|
||||||
|
/// template <typename T>
|
||||||
|
/// struct ContiguousNDArray {
|
||||||
|
/// size_t ndims;
|
||||||
|
/// size_t* shape;
|
||||||
|
/// T* data;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This is how it should be done:
|
||||||
|
/// ```ignore
|
||||||
|
/// pub struct ContiguousNDArrayFields<'ctx, F: FieldTraversal<'ctx>, Item: Model<'ctx>> {
|
||||||
|
/// pub ndims: F::Out<Int<SizeT>>,
|
||||||
|
/// pub shape: F::Out<Ptr<Int<SizeT>>>,
|
||||||
|
/// pub data: F::Out<Ptr<Item>>,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// /// An ndarray without strides and non-opaque `data` field in NAC3.
|
||||||
|
/// #[derive(Debug, Clone, Copy)]
|
||||||
|
/// pub struct ContiguousNDArray<M> {
|
||||||
|
/// /// [`Model`] of the items.
|
||||||
|
/// pub item: M,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl<'ctx, Item: Model<'ctx>> StructKind<'ctx> for ContiguousNDArray<Item> {
|
||||||
|
/// type Fields<F: FieldTraversal<'ctx>> = ContiguousNDArrayFields<'ctx, F, Item>;
|
||||||
|
///
|
||||||
|
/// fn traverse_fields<F: FieldTraversal<'ctx>>(&self, traversal: &mut F) -> Self::Fields<F> {
|
||||||
|
/// // The order of `traversal.add*` is important
|
||||||
|
/// Self::Fields {
|
||||||
|
/// ndims: traversal.add_auto("ndims"),
|
||||||
|
/// shape: traversal.add_auto("shape"),
|
||||||
|
/// data: traversal.add("data", Ptr(self.item)),
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// The [`FieldTraversal`] here is a mechanism to allow the fields of `ContiguousNDArrayFields` to be
|
||||||
|
/// traversed to do useful work such as:
|
||||||
|
///
|
||||||
|
/// - To create the [`StructType`] of `ContiguousNDArray` by collecting [`BasicType`]s of the fields.
|
||||||
|
/// - To enable the `.gep(ctx, |f| f.ndims).store(ctx, ...)` syntax.
|
||||||
|
///
|
||||||
|
/// Suppose now that you have defined `ContiguousNDArray` and you want to allocate a `ContiguousNDArray`
|
||||||
|
/// with dtype `float64` in LLVM, this is how you do it:
|
||||||
|
/// ```rust
|
||||||
|
/// type F64NDArray = ContiguousNDArray<Float<Float64>>; // Type alias for leaner documentation
|
||||||
|
/// let model: Struct<F64NDArray> = Struct(ContigousNDArray { item: Float(Float64) });
|
||||||
|
/// // In fact you may even do `let model = Struct<F64NDArray>::default()`.
|
||||||
|
/// let ndarray: Instance<'ctx, Ptr<F64NDArray>> = model.alloca(generator, ctx);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ...and here is how you may manipulate/access `ndarray`:
|
||||||
|
///
|
||||||
|
/// (NOTE: some arguments have been omitted)
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// // Get `&ndarray->data`
|
||||||
|
/// ndarray.gep(|f| f.data); // type: Instance<'ctx, Ptr<Float<Float64>>>
|
||||||
|
///
|
||||||
|
/// // Get `ndarray->ndims`
|
||||||
|
/// ndarray.get(|f| f.ndims); // type: Instance<'ctx, Int<SizeT>>
|
||||||
|
///
|
||||||
|
/// // Get `&ndarray->ndims`
|
||||||
|
/// ndarray.gep(|f| f.ndims); // type: Instance<'ctx, Ptr<Int<SizeT>>>
|
||||||
|
///
|
||||||
|
/// // Get `ndarray->shape[0]`
|
||||||
|
/// ndarray.get(|f| f.shape).get_index_const(0); // Instance<'ctx, Int<SizeT>>
|
||||||
|
///
|
||||||
|
/// // Get `&ndarray->shape[2]`
|
||||||
|
/// ndarray.get(|f| f.shape).offset_const(2); // Instance<'ctx, Ptr<Int<SizeT>>>
|
||||||
|
///
|
||||||
|
/// // Do `ndarray->ndims = 3;`
|
||||||
|
/// let num_3 = Int(SizeT).const_int(3);
|
||||||
|
/// ndarray.set(|f| f.ndims, num_3);
|
||||||
|
/// ```
|
||||||
|
pub trait StructKind<'ctx>: fmt::Debug + Clone + Copy {
|
||||||
|
/// The associated fields of this struct.
|
||||||
|
type Fields<F: FieldTraversal<'ctx>>;
|
||||||
|
|
||||||
|
/// Traverse map through all fields of this [`StructKind`].
|
||||||
|
fn traverse_fields<F: FieldTraversal<'ctx>>(&self, traversal: &mut F) -> Self::Fields<F>;
|
||||||
|
|
||||||
|
/// Get a convenience structure to get a struct field's GEP index through its corresponding Rust field.
|
||||||
|
fn fields(&self) -> Self::Fields<GepFieldTraversal> {
|
||||||
|
self.traverse_fields(&mut GepFieldTraversal { gep_index_counter: 0 })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the LLVM [`StructType`] of this [`StructKind`].
|
||||||
|
fn get_struct_type<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
) -> StructType<'ctx> {
|
||||||
|
let mut traversal = TypeFieldTraversal { generator, ctx, field_types: Vec::new() };
|
||||||
|
self.traverse_fields(&mut traversal);
|
||||||
|
|
||||||
|
ctx.struct_type(&traversal.field_types, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct Struct<S>(pub S);
|
||||||
|
|
||||||
|
impl<'ctx, S: StructKind<'ctx>> Struct<S> {
|
||||||
|
/// Create a constant struct value.
|
||||||
|
///
|
||||||
|
/// This function also validates `fields` and panic when there is something wrong.
|
||||||
|
pub fn const_struct<G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
fields: &[BasicValueEnum<'ctx>],
|
||||||
|
) -> Instance<'ctx, Self> {
|
||||||
|
// NOTE: There *could* have been a functor `F<M> = Instance<'ctx, M>` for `S::Fields<F>`
|
||||||
|
// to create a more user-friendly interface, but Rust's type system is not sophisticated enough
|
||||||
|
// and if you try doing that Rust would force you put lifetimes everywhere.
|
||||||
|
let val = ctx.const_struct(fields, false);
|
||||||
|
self.check_value(generator, ctx, val).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx, S: StructKind<'ctx>> Model<'ctx> for Struct<S> {
|
||||||
|
type Value = StructValue<'ctx>;
|
||||||
|
type Type = StructType<'ctx>;
|
||||||
|
|
||||||
|
fn get_type<G: CodeGenerator + ?Sized>(&self, generator: &G, ctx: &'ctx Context) -> Self::Type {
|
||||||
|
self.0.get_struct_type(generator, ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_type<T: BasicType<'ctx>, G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
ty: T,
|
||||||
|
) -> Result<(), ModelError> {
|
||||||
|
let ty = ty.as_basic_type_enum();
|
||||||
|
let Ok(ty) = StructType::try_from(ty) else {
|
||||||
|
return Err(ModelError(format!("Expecting StructType, but got {ty:?}")));
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut traversal =
|
||||||
|
CheckTypeFieldTraversal { generator, ctx, index: 0, errors: Vec::new(), scrutinee: ty };
|
||||||
|
self.0.traverse_fields(&mut traversal);
|
||||||
|
|
||||||
|
let exp_num_fields = traversal.index;
|
||||||
|
let got_num_fields = u32::try_from(ty.get_field_types().len()).unwrap();
|
||||||
|
if exp_num_fields != got_num_fields {
|
||||||
|
return Err(ModelError(format!(
|
||||||
|
"Expecting StructType with {exp_num_fields} field(s), but got {got_num_fields}"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !traversal.errors.is_empty() {
|
||||||
|
// Currently, only the first error is reported.
|
||||||
|
return Err(traversal.errors[0].clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx, S: StructKind<'ctx>> Instance<'ctx, Struct<S>> {
|
||||||
|
/// Get a field with [`StructValue::get_field_at_index`].
|
||||||
|
pub fn get_field<G: CodeGenerator + ?Sized, M, GetField>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
get_field: GetField,
|
||||||
|
) -> Instance<'ctx, M>
|
||||||
|
where
|
||||||
|
M: Model<'ctx>,
|
||||||
|
GetField: FnOnce(S::Fields<GepFieldTraversal>) -> GepField<M>,
|
||||||
|
{
|
||||||
|
let field = get_field(self.model.0.fields());
|
||||||
|
let val = self.value.get_field_at_index(field.gep_index as u32).unwrap();
|
||||||
|
field.model.check_value(generator, ctx, val).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx, S: StructKind<'ctx>> Instance<'ctx, Ptr<Struct<S>>> {
|
||||||
|
/// Get a pointer to a field with [`Builder::build_in_bounds_gep`].
|
||||||
|
pub fn gep<M, GetField>(
|
||||||
|
&self,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
get_field: GetField,
|
||||||
|
) -> Instance<'ctx, Ptr<M>>
|
||||||
|
where
|
||||||
|
M: Model<'ctx>,
|
||||||
|
GetField: FnOnce(S::Fields<GepFieldTraversal>) -> GepField<M>,
|
||||||
|
{
|
||||||
|
let field = get_field(self.model.0 .0.fields());
|
||||||
|
let llvm_i32 = ctx.ctx.i32_type();
|
||||||
|
|
||||||
|
let ptr = unsafe {
|
||||||
|
ctx.builder
|
||||||
|
.build_in_bounds_gep(
|
||||||
|
self.value,
|
||||||
|
&[llvm_i32.const_zero(), llvm_i32.const_int(field.gep_index, false)],
|
||||||
|
field.name,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
Ptr(field.model).believe_value(ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience function equivalent to `.gep(...).load(...)`.
|
||||||
|
pub fn get<M, GetField, G: CodeGenerator + ?Sized>(
|
||||||
|
&self,
|
||||||
|
generator: &mut G,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
get_field: GetField,
|
||||||
|
) -> Instance<'ctx, M>
|
||||||
|
where
|
||||||
|
M: Model<'ctx>,
|
||||||
|
GetField: FnOnce(S::Fields<GepFieldTraversal>) -> GepField<M>,
|
||||||
|
{
|
||||||
|
self.gep(ctx, get_field).load(generator, ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience function equivalent to `.gep(...).store(...)`.
|
||||||
|
pub fn set<M, GetField>(
|
||||||
|
&self,
|
||||||
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
get_field: GetField,
|
||||||
|
value: Instance<'ctx, M>,
|
||||||
|
) where
|
||||||
|
M: Model<'ctx>,
|
||||||
|
GetField: FnOnce(S::Fields<GepFieldTraversal>) -> GepField<M>,
|
||||||
|
{
|
||||||
|
self.gep(ctx, get_field).store(ctx, value);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue