From d3a5c5a48a391d812b200175b39e7311b6feeefd Mon Sep 17 00:00:00 2001 From: lyken Date: Tue, 16 Jul 2024 13:05:16 +0800 Subject: [PATCH] core: document codegen/model (incomplete) --- nac3core/src/codegen/model/core.rs | 16 +++++- nac3core/src/codegen/model/gep.rs | 50 +++++++++++++----- nac3core/src/codegen/model/int.rs | 76 ++++++++++++++++++--------- nac3core/src/codegen/model/pointer.rs | 9 ++++ 4 files changed, 113 insertions(+), 38 deletions(-) diff --git a/nac3core/src/codegen/model/core.rs b/nac3core/src/codegen/model/core.rs index 5f7628e3..ddbed4de 100644 --- a/nac3core/src/codegen/model/core.rs +++ b/nac3core/src/codegen/model/core.rs @@ -22,10 +22,13 @@ pub trait ModelValue<'ctx>: Clone + Copy { fn get_llvm_value(&self) -> BasicValueEnum<'ctx>; } -// Should have been within [`Model`], +// Should have been within [`Model<'ctx>`], // but rust object safety requirements made it necessary to // split this interface out 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( &self, ctx: &'ctx Context, @@ -33,12 +36,22 @@ pub trait CanCheckLLVMType<'ctx> { ) -> Result<(), String>; } +/// A [`Model`] is a type-safe concrete representation of a complex LLVM type. 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>; + /// Get the [`BasicTypeEnum<'ctx>`] this [`Model<'ctx>`] is representing. 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; + /// Build an instruction to allocate a value of [`Model::get_llvm_type`]. fn alloca(&self, ctx: &CodeGenContext<'ctx, '_>, name: &str) -> Pointer<'ctx, Self> { Pointer { 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( &self, ctx: &CodeGenContext<'ctx, '_>, diff --git a/nac3core/src/codegen/model/gep.rs b/nac3core/src/codegen/model/gep.rs index 1e201a0e..5c34669b 100644 --- a/nac3core/src/codegen/model/gep.rs +++ b/nac3core/src/codegen/model/gep.rs @@ -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 { + /// The type of the Rust `struct` that holds all the fields of this LLVM struct. type Fields; + /// A cosmetic name for this struct. + /// TODO: Currently unused. To be used in error reporting. fn struct_name(&self) -> &'static str; 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) } + /// Get the LLVM struct type this [`IsStruct<'ctx>`] is representing. fn get_struct_type(&self, ctx: &'ctx Context) -> StructType<'ctx> { let mut builder = FieldBuilder::new(ctx, self.struct_name()); 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) } + /// Check if `scrutinee` matches the [`StructType<'ctx>`] this [`IsStruct<'ctx>`] is representing. fn check_struct_type( &self, 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)] pub struct StructModel(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 { fn check_llvm_type( &self, @@ -164,7 +161,36 @@ impl<'ctx, S: IsStruct<'ctx>> Model<'ctx> for StructModel { } } +#[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> { + /// 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>>; + /// 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( &self, ctx: &CodeGenContext<'ctx, '_>, diff --git a/nac3core/src/codegen/model/int.rs b/nac3core/src/codegen/model/int.rs index a790cd54..1a10fc65 100644 --- a/nac3core/src/codegen/model/int.rs +++ b/nac3core/src/codegen/model/int.rs @@ -8,6 +8,7 @@ use crate::codegen::CodeGenContext; use super::core::*; +/// Helper function to check if `scrutinee` is the same as `expected_int_type` fn check_int_llvm_type<'ctx>( scrutinee: AnyTypeEnum<'ctx>, expected_int_type: IntType<'ctx>, @@ -29,6 +30,8 @@ fn check_int_llvm_type<'ctx>( 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>( value: AnyValueEnum<'ctx>, expected_int_type: IntType<'ctx>, @@ -40,18 +43,18 @@ fn review_int_llvm_value<'ctx>( 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)] pub struct IntModel<'ctx>(pub IntType<'ctx>); +/// An inhabitant of an [`IntModel<'ctx>`] #[derive(Debug, Clone, Copy)] 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> { fn check_llvm_type( &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> { #[must_use] pub fn signed_cast_to_int( @@ -104,12 +114,18 @@ impl<'ctx> Int<'ctx> { } } +// Extra utilities for [`IntModel<'ctx>`] impl<'ctx> IntModel<'ctx> { + /// Create a constant value that inhabits this [`IntModel<'ctx>`]. #[must_use] pub fn constant(&self, value: u64) -> Int<'ctx> { 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] pub fn same_as(&self, other: IntModel<'ctx>) -> bool { // 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)] pub struct FixedIntModel(pub T); @@ -126,24 +146,6 @@ impl FixedIntModel { } } -#[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 { fn check_llvm_type( &self, @@ -173,6 +175,30 @@ impl<'ctx, T: IsFixedInt> FixedIntModel { } } +/// 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> { pub fn to_int(self) -> Int<'ctx> { 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)] pub struct Bool; diff --git a/nac3core/src/codegen/model/pointer.rs b/nac3core/src/codegen/model/pointer.rs index add6bbf5..c8eb9371 100644 --- a/nac3core/src/codegen/model/pointer.rs +++ b/nac3core/src/codegen/model/pointer.rs @@ -9,12 +9,19 @@ use crate::codegen::CodeGenContext; use super::core::*; +/// An inhabitant of [`PointerModel`] #[derive(Debug, Clone, Copy)] pub struct Pointer<'ctx, E: Model<'ctx>> { pub element: E, 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)] pub struct PointerModel(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> { + /// Build an instruction to store a value into this pointer pub fn store(&self, ctx: &CodeGenContext<'ctx, '_>, val: E::Value) { 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 { let val = ctx.builder.build_load(self.value, name).unwrap(); self.element.review(ctx.ctx, val.as_any_value_enum())