forked from M-Labs/nac3
core: document codegen/model (incomplete)
This commit is contained in:
parent
496171a4a5
commit
d3a5c5a48a
|
@ -22,10 +22,13 @@ pub trait ModelValue<'ctx>: Clone + Copy {
|
||||||
fn get_llvm_value(&self) -> BasicValueEnum<'ctx>;
|
fn get_llvm_value(&self) -> BasicValueEnum<'ctx>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should have been within [`Model<ctx>`],
|
// Should have been within [`Model<'ctx>`],
|
||||||
// but rust object safety requirements made it necessary to
|
// but rust object safety requirements made it necessary to
|
||||||
// split this interface out
|
// split this interface out
|
||||||
pub trait CanCheckLLVMType<'ctx> {
|
pub trait CanCheckLLVMType<'ctx> {
|
||||||
|
/// Check if `scrutinee` matches the same LLVM type of this [`Model<'ctx>`].
|
||||||
|
///
|
||||||
|
/// If they don't not match, a human-readable error message is returned.
|
||||||
fn check_llvm_type(
|
fn check_llvm_type(
|
||||||
&self,
|
&self,
|
||||||
ctx: &'ctx Context,
|
ctx: &'ctx Context,
|
||||||
|
@ -33,12 +36,22 @@ pub trait CanCheckLLVMType<'ctx> {
|
||||||
) -> Result<(), String>;
|
) -> Result<(), String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [`Model`] is a type-safe concrete representation of a complex LLVM type.
|
||||||
pub trait Model<'ctx>: Clone + Copy + CanCheckLLVMType<'ctx> + Sized {
|
pub trait Model<'ctx>: Clone + Copy + CanCheckLLVMType<'ctx> + Sized {
|
||||||
|
/// The values that inhabit this [`Model<'ctx>`].
|
||||||
|
///
|
||||||
|
/// ...that is the type of wrapper that wraps the LLVM values that inhabit [`Model<'ctx>::get_llvm_type()`].
|
||||||
type Value: ModelValue<'ctx>;
|
type Value: ModelValue<'ctx>;
|
||||||
|
|
||||||
|
/// Get the [`BasicTypeEnum<'ctx>`] this [`Model<'ctx>`] is representing.
|
||||||
fn get_llvm_type(&self, ctx: &'ctx Context) -> BasicTypeEnum<'ctx>;
|
fn get_llvm_type(&self, ctx: &'ctx Context) -> BasicTypeEnum<'ctx>;
|
||||||
|
|
||||||
|
/// Cast an [`AnyValueEnum<'ctx>`] into a [`Self::Value`].
|
||||||
|
///
|
||||||
|
/// Panics if `value` cannot pass [`CanCheckLLVMType::check_llvm_type()`].
|
||||||
fn review(&self, ctx: &'ctx Context, value: AnyValueEnum<'ctx>) -> Self::Value;
|
fn review(&self, ctx: &'ctx Context, value: AnyValueEnum<'ctx>) -> Self::Value;
|
||||||
|
|
||||||
|
/// Build an instruction to allocate a value of [`Model::get_llvm_type`].
|
||||||
fn alloca(&self, ctx: &CodeGenContext<'ctx, '_>, name: &str) -> Pointer<'ctx, Self> {
|
fn alloca(&self, ctx: &CodeGenContext<'ctx, '_>, name: &str) -> Pointer<'ctx, Self> {
|
||||||
Pointer {
|
Pointer {
|
||||||
element: *self,
|
element: *self,
|
||||||
|
@ -46,6 +59,7 @@ pub trait Model<'ctx>: Clone + Copy + CanCheckLLVMType<'ctx> + Sized {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Build an instruction to allocate an array of [`Model::get_llvm_type`].
|
||||||
fn array_alloca(
|
fn array_alloca(
|
||||||
&self,
|
&self,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
|
|
@ -66,9 +66,13 @@ impl<'ctx> FieldBuilder<'ctx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A marker trait to mark singleton struct that describes a particular LLVM structure.
|
||||||
pub trait IsStruct<'ctx>: Clone + Copy {
|
pub trait IsStruct<'ctx>: Clone + Copy {
|
||||||
|
/// The type of the Rust `struct` that holds all the fields of this LLVM struct.
|
||||||
type Fields;
|
type Fields;
|
||||||
|
|
||||||
|
/// A cosmetic name for this struct.
|
||||||
|
/// TODO: Currently unused. To be used in error reporting.
|
||||||
fn struct_name(&self) -> &'static str;
|
fn struct_name(&self) -> &'static str;
|
||||||
|
|
||||||
fn build_fields(&self, builder: &mut FieldBuilder<'ctx>) -> Self::Fields;
|
fn build_fields(&self, builder: &mut FieldBuilder<'ctx>) -> Self::Fields;
|
||||||
|
@ -78,6 +82,7 @@ pub trait IsStruct<'ctx>: Clone + Copy {
|
||||||
self.build_fields(&mut builder)
|
self.build_fields(&mut builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the LLVM struct type this [`IsStruct<'ctx>`] is representing.
|
||||||
fn get_struct_type(&self, ctx: &'ctx Context) -> StructType<'ctx> {
|
fn get_struct_type(&self, ctx: &'ctx Context) -> StructType<'ctx> {
|
||||||
let mut builder = FieldBuilder::new(ctx, self.struct_name());
|
let mut builder = FieldBuilder::new(ctx, self.struct_name());
|
||||||
self.build_fields(&mut builder); // Self::Fields is discarded
|
self.build_fields(&mut builder); // Self::Fields is discarded
|
||||||
|
@ -86,6 +91,7 @@ pub trait IsStruct<'ctx>: Clone + Copy {
|
||||||
ctx.struct_type(&field_types, false)
|
ctx.struct_type(&field_types, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if `scrutinee` matches the [`StructType<'ctx>`] this [`IsStruct<'ctx>`] is representing.
|
||||||
fn check_struct_type(
|
fn check_struct_type(
|
||||||
&self,
|
&self,
|
||||||
ctx: &'ctx Context,
|
ctx: &'ctx Context,
|
||||||
|
@ -118,21 +124,12 @@ pub trait IsStruct<'ctx>: Clone + Copy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [`Model<'ctx>`] that represents an LLVM struct.
|
||||||
|
///
|
||||||
|
/// `self.0` contains a [`IsStruct<'ctx>`] that gives the details of the LLVM struct.
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct StructModel<S>(pub S);
|
pub struct StructModel<S>(pub S);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct Struct<'ctx, S> {
|
|
||||||
pub structure: S,
|
|
||||||
pub value: StructValue<'ctx>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx, S: IsStruct<'ctx>> ModelValue<'ctx> for Struct<'ctx, S> {
|
|
||||||
fn get_llvm_value(&self) -> BasicValueEnum<'ctx> {
|
|
||||||
self.value.as_basic_value_enum()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx, S: IsStruct<'ctx>> CanCheckLLVMType<'ctx> for StructModel<S> {
|
impl<'ctx, S: IsStruct<'ctx>> CanCheckLLVMType<'ctx> for StructModel<S> {
|
||||||
fn check_llvm_type(
|
fn check_llvm_type(
|
||||||
&self,
|
&self,
|
||||||
|
@ -164,7 +161,36 @@ impl<'ctx, S: IsStruct<'ctx>> Model<'ctx> for StructModel<S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Struct<'ctx, S> {
|
||||||
|
pub structure: S,
|
||||||
|
pub value: StructValue<'ctx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx, S: IsStruct<'ctx>> ModelValue<'ctx> for Struct<'ctx, S> {
|
||||||
|
fn get_llvm_value(&self) -> BasicValueEnum<'ctx> {
|
||||||
|
self.value.as_basic_value_enum()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'ctx, S: IsStruct<'ctx>> Pointer<'ctx, StructModel<S>> {
|
impl<'ctx, S: IsStruct<'ctx>> Pointer<'ctx, StructModel<S>> {
|
||||||
|
/// Build an instruction that does `getelementptr` on an LLVM structure referenced by this pointer.
|
||||||
|
///
|
||||||
|
/// This provides a nice syntax to chain up `getelementptr` in an intuitive and type-safe way:
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// let ctx: &CodeGenContext<'ctx, '_>;
|
||||||
|
/// let ndarray: Pointer<'ctx, StructModel<NpArray<'ctx>>>;
|
||||||
|
/// ndarray.gep(ctx, |f| f.ndims).store();
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// You might even write chains `gep`, i.e.,
|
||||||
|
/// ```ignore
|
||||||
|
/// my_struct
|
||||||
|
/// .gep(ctx, |f| f.thing1)
|
||||||
|
/// .gep(ctx, |f| f.value)
|
||||||
|
/// .store(ctx, my_value) // Equivalent to `my_struct.thing1.value = my_value`
|
||||||
|
/// ```
|
||||||
pub fn gep<E, GetFieldFn>(
|
pub fn gep<E, GetFieldFn>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &CodeGenContext<'ctx, '_>,
|
ctx: &CodeGenContext<'ctx, '_>,
|
||||||
|
|
|
@ -8,6 +8,7 @@ use crate::codegen::CodeGenContext;
|
||||||
|
|
||||||
use super::core::*;
|
use super::core::*;
|
||||||
|
|
||||||
|
/// Helper function to check if `scrutinee` is the same as `expected_int_type`
|
||||||
fn check_int_llvm_type<'ctx>(
|
fn check_int_llvm_type<'ctx>(
|
||||||
scrutinee: AnyTypeEnum<'ctx>,
|
scrutinee: AnyTypeEnum<'ctx>,
|
||||||
expected_int_type: IntType<'ctx>,
|
expected_int_type: IntType<'ctx>,
|
||||||
|
@ -29,6 +30,8 @@ fn check_int_llvm_type<'ctx>(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper function to cast `scrutinee` is into an [`IntValue<'ctx>`].
|
||||||
|
/// The LLVM type of `scrutinee` will be checked with [`check_int_llvm_type`].
|
||||||
fn review_int_llvm_value<'ctx>(
|
fn review_int_llvm_value<'ctx>(
|
||||||
value: AnyValueEnum<'ctx>,
|
value: AnyValueEnum<'ctx>,
|
||||||
expected_int_type: IntType<'ctx>,
|
expected_int_type: IntType<'ctx>,
|
||||||
|
@ -40,18 +43,18 @@ fn review_int_llvm_value<'ctx>(
|
||||||
Ok(value.into_int_value())
|
Ok(value.into_int_value())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A model representing an [`IntType<'ctx>`].
|
||||||
|
///
|
||||||
|
/// Also see [`FixedIntModel`], which is more constrained than [`IntModel`]
|
||||||
|
/// but provides more type-safe mechanisms and even auto-derivation of [`BasicTypeEnum<'ctx>`]
|
||||||
|
/// for creating LLVM structures.
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct IntModel<'ctx>(pub IntType<'ctx>);
|
pub struct IntModel<'ctx>(pub IntType<'ctx>);
|
||||||
|
|
||||||
|
/// An inhabitant of an [`IntModel<'ctx>`]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Int<'ctx>(pub IntValue<'ctx>);
|
pub struct Int<'ctx>(pub IntValue<'ctx>);
|
||||||
|
|
||||||
impl<'ctx> ModelValue<'ctx> for Int<'ctx> {
|
|
||||||
fn get_llvm_value(&self) -> BasicValueEnum<'ctx> {
|
|
||||||
self.0.as_basic_value_enum()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx> CanCheckLLVMType<'ctx> for IntModel<'ctx> {
|
impl<'ctx> CanCheckLLVMType<'ctx> for IntModel<'ctx> {
|
||||||
fn check_llvm_type(
|
fn check_llvm_type(
|
||||||
&self,
|
&self,
|
||||||
|
@ -76,6 +79,13 @@ impl<'ctx> Model<'ctx> for IntModel<'ctx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'ctx> ModelValue<'ctx> for Int<'ctx> {
|
||||||
|
fn get_llvm_value(&self) -> BasicValueEnum<'ctx> {
|
||||||
|
self.0.as_basic_value_enum()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extra utilities for [`Int<'ctx>`]
|
||||||
impl<'ctx> Int<'ctx> {
|
impl<'ctx> Int<'ctx> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn signed_cast_to_int(
|
pub fn signed_cast_to_int(
|
||||||
|
@ -104,12 +114,18 @@ impl<'ctx> Int<'ctx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extra utilities for [`IntModel<'ctx>`]
|
||||||
impl<'ctx> IntModel<'ctx> {
|
impl<'ctx> IntModel<'ctx> {
|
||||||
|
/// Create a constant value that inhabits this [`IntModel<'ctx>`].
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn constant(&self, value: u64) -> Int<'ctx> {
|
pub fn constant(&self, value: u64) -> Int<'ctx> {
|
||||||
Int(self.0.const_int(value, false))
|
Int(self.0.const_int(value, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if `other` is fully compatible with this [`IntModel<'ctx>`].
|
||||||
|
///
|
||||||
|
/// This simply checks if the underlying [`IntType<'ctx>`] has
|
||||||
|
/// the same number of bits.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn same_as(&self, other: IntModel<'ctx>) -> bool {
|
pub fn same_as(&self, other: IntModel<'ctx>) -> bool {
|
||||||
// TODO: or `self.0 == other.0` would also work?
|
// TODO: or `self.0 == other.0` would also work?
|
||||||
|
@ -117,6 +133,10 @@ impl<'ctx> IntModel<'ctx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A model representing a compile-time known [`IntType<'ctx>`].
|
||||||
|
///
|
||||||
|
/// Also see [`IntModel`], which is less constrained than [`FixedIntModel`],
|
||||||
|
/// but enables one to handle [`IntType<'ctx>`] that could be dynamic
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
pub struct FixedIntModel<T>(pub T);
|
pub struct FixedIntModel<T>(pub T);
|
||||||
|
|
||||||
|
@ -126,24 +146,6 @@ impl<T: IsFixedInt> FixedIntModel<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct FixedInt<'ctx, T: IsFixedInt> {
|
|
||||||
pub int: T,
|
|
||||||
pub value: IntValue<'ctx>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default instance is to enable `FieldBuilder::add_field_auto`
|
|
||||||
pub trait IsFixedInt: Clone + Copy + Default {
|
|
||||||
fn get_int_type(ctx: &Context) -> IntType<'_>;
|
|
||||||
fn get_bit_width() -> u32; // This is required, instead of only relying on get_int_type
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx, T: IsFixedInt> ModelValue<'ctx> for FixedInt<'ctx, T> {
|
|
||||||
fn get_llvm_value(&self) -> BasicValueEnum<'ctx> {
|
|
||||||
self.value.as_basic_value_enum()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'ctx, T: IsFixedInt> CanCheckLLVMType<'ctx> for FixedIntModel<T> {
|
impl<'ctx, T: IsFixedInt> CanCheckLLVMType<'ctx> for FixedIntModel<T> {
|
||||||
fn check_llvm_type(
|
fn check_llvm_type(
|
||||||
&self,
|
&self,
|
||||||
|
@ -173,6 +175,30 @@ impl<'ctx, T: IsFixedInt> FixedIntModel<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An inhabitant of [`FixedIntModel<'ctx>`]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct FixedInt<'ctx, T: IsFixedInt> {
|
||||||
|
pub int: T,
|
||||||
|
pub value: IntValue<'ctx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A marker trait to mark singleton struct that describes a particular fixed integer type.
|
||||||
|
/// See [`Bool`], [`Byte`], [`Int32`], etc.
|
||||||
|
///
|
||||||
|
/// The [`Default`] trait is to enable auto-derivations for utilities like
|
||||||
|
/// [`FieldBuilder::add_field_auto`]
|
||||||
|
pub trait IsFixedInt: Clone + Copy + Default {
|
||||||
|
fn get_int_type(ctx: &Context) -> IntType<'_>;
|
||||||
|
fn get_bit_width() -> u32; // This is required, instead of only relying on get_int_type
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx, T: IsFixedInt> ModelValue<'ctx> for FixedInt<'ctx, T> {
|
||||||
|
fn get_llvm_value(&self) -> BasicValueEnum<'ctx> {
|
||||||
|
self.value.as_basic_value_enum()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extra utilities for [`FixedInt<'ctx, T>`]
|
||||||
impl<'ctx, T: IsFixedInt> FixedInt<'ctx, T> {
|
impl<'ctx, T: IsFixedInt> FixedInt<'ctx, T> {
|
||||||
pub fn to_int(self) -> Int<'ctx> {
|
pub fn to_int(self) -> Int<'ctx> {
|
||||||
Int(self.value)
|
Int(self.value)
|
||||||
|
@ -194,7 +220,7 @@ impl<'ctx, T: IsFixedInt> FixedInt<'ctx, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some pre-defined fixed ints
|
// Some pre-defined fixed integers
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
pub struct Bool;
|
pub struct Bool;
|
||||||
|
|
|
@ -9,12 +9,19 @@ use crate::codegen::CodeGenContext;
|
||||||
|
|
||||||
use super::core::*;
|
use super::core::*;
|
||||||
|
|
||||||
|
/// An inhabitant of [`PointerModel<E>`]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Pointer<'ctx, E: Model<'ctx>> {
|
pub struct Pointer<'ctx, E: Model<'ctx>> {
|
||||||
pub element: E,
|
pub element: E,
|
||||||
pub value: PointerValue<'ctx>,
|
pub value: PointerValue<'ctx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [`Model<'ctx>`] representing an LLVM [`PointerType<'ctx>`]
|
||||||
|
/// with *full* information on the element u
|
||||||
|
///
|
||||||
|
/// [`self.0`] contains [`Model<'ctx>`] that represents the
|
||||||
|
/// LLVM type of element of the [`PointerType<'ctx>`] is pointing at
|
||||||
|
/// (like `PointerType<'ctx>::get_element_type()`, but abstracted as a [`Model<'ctx>`]).
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
pub struct PointerModel<E>(pub E);
|
pub struct PointerModel<E>(pub E);
|
||||||
|
|
||||||
|
@ -25,10 +32,12 @@ impl<'ctx, E: Model<'ctx>> ModelValue<'ctx> for Pointer<'ctx, E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx, E: Model<'ctx>> Pointer<'ctx, E> {
|
impl<'ctx, E: Model<'ctx>> Pointer<'ctx, E> {
|
||||||
|
/// Build an instruction to store a value into this pointer
|
||||||
pub fn store(&self, ctx: &CodeGenContext<'ctx, '_>, val: E::Value) {
|
pub fn store(&self, ctx: &CodeGenContext<'ctx, '_>, val: E::Value) {
|
||||||
ctx.builder.build_store(self.value, val.get_llvm_value()).unwrap();
|
ctx.builder.build_store(self.value, val.get_llvm_value()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Build an instruction to load a value from this pointer
|
||||||
pub fn load(&self, ctx: &CodeGenContext<'ctx, '_>, name: &str) -> E::Value {
|
pub fn load(&self, ctx: &CodeGenContext<'ctx, '_>, name: &str) -> E::Value {
|
||||||
let val = ctx.builder.build_load(self.value, name).unwrap();
|
let val = ctx.builder.build_load(self.value, name).unwrap();
|
||||||
self.element.review(ctx.ctx, val.as_any_value_enum())
|
self.element.review(ctx.ctx, val.as_any_value_enum())
|
||||||
|
|
Loading…
Reference in New Issue