forked from M-Labs/nac3
[core] introduce models
This commit is contained in:
parent
581b2f7bb2
commit
0bc98c447d
nac3core/src/codegen
@ -46,6 +46,7 @@ pub mod extern_fns;
|
||||
mod generator;
|
||||
pub mod irrt;
|
||||
pub mod llvm_intrinsics;
|
||||
pub mod model;
|
||||
pub mod numpy;
|
||||
pub mod stmt;
|
||||
|
||||
|
41
nac3core/src/codegen/model/any.rs
Normal file
41
nac3core/src/codegen/model/any.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use inkwell::{
|
||||
context::Context,
|
||||
types::{BasicType, BasicTypeEnum},
|
||||
values::BasicValueEnum,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
use crate::codegen::CodeGenerator;
|
||||
|
||||
/// A [`Model`] of any [`BasicTypeEnum`].
|
||||
///
|
||||
/// Use this when it is infeasible to use model abstractions.
|
||||
#[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 llvm_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)))
|
||||
}
|
||||
}
|
||||
}
|
146
nac3core/src/codegen/model/array.rs
Normal file
146
nac3core/src/codegen/model/array.rs
Normal file
@ -0,0 +1,146 @@
|
||||
use std::fmt;
|
||||
|
||||
use inkwell::{
|
||||
context::Context,
|
||||
types::{ArrayType, BasicType, BasicTypeEnum},
|
||||
values::{ArrayValue, IntValue},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
use crate::codegen::{CodeGenContext, CodeGenerator};
|
||||
|
||||
/// Trait for Rust structs identifying length values for [`Array`].
|
||||
pub trait ArrayLen: fmt::Debug + Clone + Copy {
|
||||
fn 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> ArrayLen for Len<N> {
|
||||
fn length(&self) -> u32 {
|
||||
N
|
||||
}
|
||||
}
|
||||
|
||||
impl ArrayLen for AnyLen {
|
||||
fn length(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// A Model for an [`ArrayType`].
|
||||
///
|
||||
/// `Len` should be of a [`LenKind`] and `Item` should be a of [`Model`].
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct Array<Len, Item> {
|
||||
/// Length of this array.
|
||||
pub len: Len,
|
||||
/// [`Model`] of the array items.
|
||||
pub item: Item,
|
||||
}
|
||||
|
||||
impl<'ctx, Len: ArrayLen, Item: Model<'ctx>> Model<'ctx> for Array<Len, Item> {
|
||||
type Value = ArrayValue<'ctx>;
|
||||
type Type = ArrayType<'ctx>;
|
||||
|
||||
fn llvm_type<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
) -> Self::Type {
|
||||
self.item.llvm_type(generator, ctx).array_type(self.len.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.length() {
|
||||
return Err(ModelError(format!(
|
||||
"Expecting ArrayType with size {}, but got an ArrayType with size {}",
|
||||
ty.len(),
|
||||
self.len.length()
|
||||
)));
|
||||
}
|
||||
|
||||
self.item
|
||||
.check_type(generator, ctx, ty.get_element_type())
|
||||
.map_err(|err| err.under_context("an ArrayType"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx, Len: ArrayLen, 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() };
|
||||
|
||||
unsafe { 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.length()),
|
||||
"Index {i} is out of bounds. Array length = {}",
|
||||
self.model.0.len.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);
|
||||
}
|
||||
}
|
207
nac3core/src/codegen/model/core.rs
Normal file
207
nac3core/src/codegen/model/core.rs
Normal file
@ -0,0 +1,207 @@
|
||||
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 [`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 their types, it is possible
|
||||
/// to tell the [`BasicType`]s 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, for example, 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/ge.
|
||||
///
|
||||
/// 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.
|
||||
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 llvm_type<G: CodeGenerator + ?Sized>(&self, generator: &G, ctx: &'ctx Context)
|
||||
-> Self::Type;
|
||||
|
||||
/// Get the number of bytes of the [`BasicType`] of this model.
|
||||
fn size_of<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &'ctx Context,
|
||||
) -> IntValue<'ctx> {
|
||||
self.llvm_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.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Caller must make sure the type of `value` and the type of this `model` are equivalent.
|
||||
#[must_use]
|
||||
unsafe 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 the [`BasicValue`] 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")
|
||||
};
|
||||
unsafe { 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.llvm_type(generator, ctx.ctx), "").unwrap();
|
||||
unsafe { 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.llvm_type(generator, ctx.ctx), len, "").unwrap();
|
||||
unsafe { 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.llvm_type(generator, ctx.ctx).as_basic_type_enum();
|
||||
let p = generator.gen_var_alloc(ctx, ty, name)?;
|
||||
unsafe { 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.llvm_type(generator, ctx.ctx).as_basic_type_enum();
|
||||
let p = generator.gen_array_var_alloc(ctx, ty, len, name)?;
|
||||
unsafe { 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.llvm_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.
|
||||
///
|
||||
/// It is guaranteed the [`BasicType`] of `value` is consistent with that of `model`.
|
||||
pub value: M::Value,
|
||||
}
|
93
nac3core/src/codegen/model/float.rs
Normal file
93
nac3core/src/codegen/model/float.rs
Normal file
@ -0,0 +1,93 @@
|
||||
use std::fmt;
|
||||
|
||||
use inkwell::{
|
||||
context::Context,
|
||||
types::{BasicType, FloatType},
|
||||
values::FloatValue,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
use crate::codegen::CodeGenerator;
|
||||
|
||||
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 llvm_type<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
) -> Self::Type {
|
||||
self.0.get_float_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) = 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(())
|
||||
}
|
||||
}
|
121
nac3core/src/codegen/model/function.rs
Normal file
121
nac3core/src/codegen/model/function.rs
Normal file
@ -0,0 +1,121 @@
|
||||
use inkwell::{
|
||||
attributes::{Attribute, AttributeLoc},
|
||||
types::{BasicMetadataTypeEnum, BasicType, FunctionType},
|
||||
values::{AnyValue, BasicMetadataValueEnum, BasicValue, BasicValueEnum, CallSiteValue},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
use super::*;
|
||||
use crate::codegen::{CodeGenContext, CodeGenerator};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Arg<'ctx> {
|
||||
ty: BasicMetadataTypeEnum<'ctx>,
|
||||
val: BasicMetadataValueEnum<'ctx>,
|
||||
}
|
||||
|
||||
/// A convenience structure to construct & call an LLVM function.
|
||||
///
|
||||
/// ### Usage
|
||||
///
|
||||
/// The syntax is like this:
|
||||
/// ```ignore
|
||||
/// let result = CallFunction::begin("my_function_name")
|
||||
/// .attrs(...)
|
||||
/// .arg(arg1)
|
||||
/// .arg(arg2)
|
||||
/// .arg(arg3)
|
||||
/// .returning("my_function_result", Int32);
|
||||
/// ```
|
||||
///
|
||||
/// The function `my_function_name` is called when `.returning()` (or its variants) is called, returning
|
||||
/// the result as an `Instance<'ctx, Int<Int32>>`.
|
||||
///
|
||||
/// If `my_function_name` has not been declared in `ctx.module`, once `.returning()` is called, a function
|
||||
/// declaration of `my_function_name` is added to `ctx.module`, where the [`FunctionType`] is deduced from
|
||||
/// the argument types and returning type.
|
||||
pub struct FnCall<'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> FnCall<'ctx, 'a, 'b, 'c, 'd, G> {
|
||||
pub fn builder(generator: &'d mut G, ctx: &'b CodeGenContext<'ctx, 'a>, name: &'c str) -> Self {
|
||||
FnCall { 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.llvm_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.llvm_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()
|
||||
}
|
||||
}
|
421
nac3core/src/codegen/model/int.rs
Normal file
421
nac3core/src/codegen/model/int.rs
Normal file
@ -0,0 +1,421 @@
|
||||
use std::{cmp::Ordering, fmt};
|
||||
|
||||
use inkwell::{
|
||||
context::Context,
|
||||
types::{BasicType, IntType},
|
||||
values::IntValue,
|
||||
IntPredicate,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
use crate::codegen::{CodeGenContext, CodeGenerator};
|
||||
|
||||
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 llvm_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,
|
||||
sign_extend: bool,
|
||||
) -> Instance<'ctx, Self> {
|
||||
let value = self.llvm_type(generator, ctx).const_int(value, sign_extend);
|
||||
unsafe { self.believe_value(value) }
|
||||
}
|
||||
|
||||
pub fn const_0<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &'ctx Context,
|
||||
) -> Instance<'ctx, Self> {
|
||||
let value = self.llvm_type(generator, ctx).const_zero();
|
||||
unsafe { 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, false)
|
||||
}
|
||||
|
||||
pub fn const_all_ones<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &'ctx Context,
|
||||
) -> Instance<'ctx, Self> {
|
||||
let value = self.llvm_type(generator, ctx).const_all_ones();
|
||||
unsafe { 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.llvm_type(generator, ctx.ctx), "")
|
||||
.unwrap();
|
||||
unsafe { 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.llvm_type(generator, ctx.ctx), "").unwrap();
|
||||
unsafe { 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.llvm_type(generator, ctx.ctx), "")
|
||||
.unwrap();
|
||||
unsafe { 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.llvm_type(generator, ctx.ctx), "").unwrap();
|
||||
unsafe { 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.llvm_type(generator, ctx.ctx), "")
|
||||
.unwrap();
|
||||
unsafe { 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.llvm_type(generator, ctx.ctx), "").unwrap();
|
||||
unsafe { 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 => unsafe { 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 => unsafe { 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, false)
|
||||
}
|
||||
|
||||
#[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, false)
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
unsafe { 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();
|
||||
unsafe { 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();
|
||||
unsafe { 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();
|
||||
unsafe { Int(Bool).believe_value(value) }
|
||||
}
|
||||
}
|
16
nac3core/src/codegen/model/mod.rs
Normal file
16
nac3core/src/codegen/model/mod.rs
Normal file
@ -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::*;
|
213
nac3core/src/codegen/model/ptr.rs
Normal file
213
nac3core/src/codegen/model/ptr.rs
Normal file
@ -0,0 +1,213 @@
|
||||
use inkwell::{
|
||||
context::Context,
|
||||
types::{BasicType, BasicTypeEnum, PointerType},
|
||||
values::{IntValue, PointerValue},
|
||||
AddressSpace,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
use crate::codegen::{llvm_intrinsics::call_memcpy_generic, CodeGenContext, CodeGenerator};
|
||||
|
||||
/// A model for [`PointerType`].
|
||||
///
|
||||
/// `Item` is the element type this pointer is pointing to, and should be of a [`Model`].
|
||||
///
|
||||
// TODO: LLVM 15: `Item` is a Rust type-hint for the LLVM type of value the `.store()/.load()` family
|
||||
// of functions return. If a truly opaque pointer is needed, tell the programmer to use `OpaquePtr`.
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct Ptr<Item>(pub Item);
|
||||
|
||||
/// An opaque pointer. Like [`Ptr`] but without any Rust type-hints about its element type.
|
||||
///
|
||||
/// `.load()/.store()` is not available for [`Instance`]s of opaque pointers.
|
||||
pub type OpaquePtr = Ptr<()>;
|
||||
|
||||
// TODO: LLVM 15: `Item: Model<'ctx>` don't even need to be a model anymore. It will only be
|
||||
// a type-hint for the `.load()/.store()` functions for the `pointee_ty`.
|
||||
//
|
||||
// See https://thedan64.github.io/inkwell/inkwell/builder/struct.Builder.html#method.build_load.
|
||||
impl<'ctx, Item: Model<'ctx>> Model<'ctx> for Ptr<Item> {
|
||||
type Value = PointerValue<'ctx>;
|
||||
type Type = PointerType<'ctx>;
|
||||
|
||||
fn llvm_type<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
) -> Self::Type {
|
||||
// TODO: LLVM 15: ctx.ptr_type(AddressSpace::default())
|
||||
self.0.llvm_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, Item: Model<'ctx>> Ptr<Item> {
|
||||
/// Return a ***constant*** nullptr.
|
||||
pub fn nullptr<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &'ctx Context,
|
||||
) -> Instance<'ctx, Ptr<Item>> {
|
||||
let ptr = self.llvm_type(generator, ctx).const_null();
|
||||
unsafe { 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<Item>> {
|
||||
// TODO: LLVM 15: Write in an impl where `Item` does not have to be `Model<'ctx>`.
|
||||
// TODO: LLVM 15: This function will only have to be:
|
||||
// ```
|
||||
// return self.believe_value(ptr);
|
||||
// ```
|
||||
let t = self.llvm_type(generator, ctx.ctx);
|
||||
let ptr = ctx.builder.build_pointer_cast(ptr, t, "").unwrap();
|
||||
unsafe { 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() };
|
||||
unsafe { 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: i64,
|
||||
) -> Instance<'ctx, Ptr<Item>> {
|
||||
let offset = ctx.ctx.i32_type().const_int(offset as u64, true);
|
||||
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: i64,
|
||||
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: i64,
|
||||
) -> 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>> {
|
||||
// TODO: LLVM 15: Write in an impl where `Item` does not have to be `Model<'ctx>`.
|
||||
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();
|
||||
unsafe { 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();
|
||||
unsafe { 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.size_of(generator, ctx.ctx);
|
||||
let itemsize = Int(SizeT).z_extend_or_truncate(generator, ctx, itemsize);
|
||||
let num_items = Int(SizeT).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);
|
||||
}
|
||||
}
|
363
nac3core/src/codegen/model/structure.rs
Normal file
363
nac3core/src/codegen/model/structure.rs
Normal file
@ -0,0 +1,363 @@
|
||||
use std::fmt;
|
||||
|
||||
use inkwell::{
|
||||
context::Context,
|
||||
types::{BasicType, BasicTypeEnum, StructType},
|
||||
values::{BasicValueEnum, StructValue},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
use crate::codegen::{CodeGenContext, CodeGenerator};
|
||||
|
||||
/// 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 Output<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::Output<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::Output<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: u32,
|
||||
/// The cosmetic name of this field.
|
||||
pub name: &'static str,
|
||||
/// The [`Model`] of this field's type.
|
||||
pub model: M,
|
||||
}
|
||||
|
||||
/// A traversal to calculate the GEP index of fields.
|
||||
pub struct GepFieldTraversal {
|
||||
/// The current GEP index.
|
||||
gep_index_counter: u32,
|
||||
}
|
||||
|
||||
impl<'ctx> FieldTraversal<'ctx> for GepFieldTraversal {
|
||||
type Output<M> = GepField<M>;
|
||||
|
||||
fn add<M: Model<'ctx>>(&mut self, name: &'static str, model: M) -> Self::Output<M> {
|
||||
let gep_index = self.gep_index_counter;
|
||||
self.gep_index_counter += 1;
|
||||
Self::Output { gep_index, name, model }
|
||||
}
|
||||
}
|
||||
|
||||
/// A traversal to collect the field types of a struct.
|
||||
///
|
||||
/// This is used to collect field types and construct the LLVM struct type with [`Context::struct_type`].
|
||||
struct TypeFieldTraversal<'ctx, 'a, G: CodeGenerator + ?Sized> {
|
||||
generator: &'a G,
|
||||
ctx: &'ctx Context,
|
||||
/// The collected field types so far in exact order.
|
||||
field_types: Vec<BasicTypeEnum<'ctx>>,
|
||||
}
|
||||
|
||||
impl<'ctx, 'a, G: CodeGenerator + ?Sized> FieldTraversal<'ctx> for TypeFieldTraversal<'ctx, 'a, G> {
|
||||
type Output<M> = (); // Checking types return nothing.
|
||||
|
||||
fn add<M: Model<'ctx>>(&mut self, _name: &'static str, model: M) -> Self::Output<M> {
|
||||
let t = model.llvm_type(self.generator, self.ctx).as_basic_type_enum();
|
||||
self.field_types.push(t);
|
||||
}
|
||||
}
|
||||
|
||||
/// A traversal to check the types of fields.
|
||||
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.
|
||||
gep_index_counter: u32,
|
||||
/// The [`StructType`] to check.
|
||||
scrutinee: StructType<'ctx>,
|
||||
/// The list of collected errors so far.
|
||||
errors: Vec<ModelError>,
|
||||
}
|
||||
|
||||
impl<'ctx, 'a, G: CodeGenerator + ?Sized> FieldTraversal<'ctx>
|
||||
for CheckTypeFieldTraversal<'ctx, 'a, G>
|
||||
{
|
||||
type Output<M> = (); // Checking types return nothing.
|
||||
|
||||
fn add<M: Model<'ctx>>(&mut self, name: &'static str, model: M) -> Self::Output<M> {
|
||||
let gep_index = self.gep_index_counter;
|
||||
self.gep_index_counter += 1;
|
||||
|
||||
if let Some(t) = self.scrutinee.get_field_type_at_index(gep_index) {
|
||||
if let Err(err) = model.check_type(self.generator, self.ctx, t) {
|
||||
self.errors
|
||||
.push(err.under_context(format!("field #{gep_index} '{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:
|
||||
/// ```ignore
|
||||
/// type F64NDArray = Struct<ContiguousNDArray<Float<Float64>>>; // Type alias for leaner documentation
|
||||
/// let model: F64NDArray = Struct(ContigousNDArray { item: Float(Float64) });
|
||||
/// 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)
|
||||
///
|
||||
/// ```ignore
|
||||
/// // 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 through all fields of this [`StructKind`].
|
||||
///
|
||||
/// Only used internally in this module for implementing other components.
|
||||
fn iter_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.
|
||||
///
|
||||
/// Only used internally in this module for implementing other components.
|
||||
fn fields(&self) -> Self::Fields<GepFieldTraversal> {
|
||||
self.iter_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.iter_fields(&mut traversal);
|
||||
|
||||
ctx.struct_type(&traversal.field_types, false)
|
||||
}
|
||||
}
|
||||
|
||||
/// A model for LLVM struct.
|
||||
///
|
||||
/// `S` should be of a [`StructKind`].
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct Struct<S>(pub S);
|
||||
|
||||
impl<'ctx, S: StructKind<'ctx>> Struct<S> {
|
||||
/// Create a constant struct value from its fields.
|
||||
///
|
||||
/// 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 llvm_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:?}")));
|
||||
};
|
||||
|
||||
// Check each field individually.
|
||||
let mut traversal = CheckTypeFieldTraversal {
|
||||
generator,
|
||||
ctx,
|
||||
gep_index_counter: 0,
|
||||
errors: Vec::new(),
|
||||
scrutinee: ty,
|
||||
};
|
||||
self.0.iter_fields(&mut traversal);
|
||||
|
||||
// Check the number of fields.
|
||||
let exp_num_fields = traversal.gep_index_counter;
|
||||
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).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(u64::from(field.gep_index), false)],
|
||||
field.name,
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
unsafe { 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
Block a user