From e4998ccec8b77f67b86ce7ad694acec58fbc7d98 Mon Sep 17 00:00:00 2001 From: lyken Date: Sun, 14 Jul 2024 16:03:43 +0800 Subject: [PATCH] core: split codegen optics into modules --- nac3core/src/codegen/irrt/classes.rs | 21 +- nac3core/src/codegen/irrt/new.rs | 6 +- nac3core/src/codegen/irrt/numpy.rs | 2 +- nac3core/src/codegen/mod.rs | 2 +- nac3core/src/codegen/optics.rs | 449 ----------------------- nac3core/src/codegen/optics/address.rs | 103 ++++++ nac3core/src/codegen/optics/core.rs | 51 +++ nac3core/src/codegen/optics/gep.rs | 53 +++ nac3core/src/codegen/optics/int.rs | 66 ++++ nac3core/src/codegen/optics/ixed.rs | 65 ++++ nac3core/src/codegen/optics/mod.rs | 15 + nac3core/src/codegen/optics/slice.rs | 36 ++ nac3core/src/codegen/optics/structure.rs | 139 +++++++ 13 files changed, 540 insertions(+), 468 deletions(-) delete mode 100644 nac3core/src/codegen/optics.rs create mode 100644 nac3core/src/codegen/optics/address.rs create mode 100644 nac3core/src/codegen/optics/core.rs create mode 100644 nac3core/src/codegen/optics/gep.rs create mode 100644 nac3core/src/codegen/optics/int.rs create mode 100644 nac3core/src/codegen/optics/ixed.rs create mode 100644 nac3core/src/codegen/optics/mod.rs create mode 100644 nac3core/src/codegen/optics/slice.rs create mode 100644 nac3core/src/codegen/optics/structure.rs diff --git a/nac3core/src/codegen/irrt/classes.rs b/nac3core/src/codegen/irrt/classes.rs index 0c28c745..b3f19339 100644 --- a/nac3core/src/codegen/irrt/classes.rs +++ b/nac3core/src/codegen/irrt/classes.rs @@ -1,9 +1,7 @@ use inkwell::types::IntType; -use crate::codegen::{ - optics::{Address, AddressLens, ArraySlice, FieldBuilder, GepGetter, IntLens, StructureOptic}, - CodeGenContext, -}; +use crate::codegen::optics::*; +use crate::codegen::CodeGenContext; #[derive(Debug, Clone)] pub struct StrLens<'ctx> { @@ -51,10 +49,7 @@ impl<'ctx> StructureOptic<'ctx> for NpArrayLens<'ctx> { "NDArray" } - fn build_fields( - &self, - builder: &mut crate::codegen::optics::FieldBuilder<'ctx>, - ) -> Self::Fields { + fn build_fields(&self, builder: &mut FieldBuilder<'ctx>) -> Self::Fields { NpArrayFields { data: builder.add_field("data", AddressLens(IntLens(builder.ctx.i8_type()))), itemsize: builder.add_field("itemsize", IntLens(builder.ctx.i8_type())), @@ -96,10 +91,7 @@ impl<'ctx> StructureOptic<'ctx> for IrrtStringLens { "String" } - fn build_fields( - &self, - builder: &mut crate::codegen::optics::FieldBuilder<'ctx>, - ) -> Self::Fields { + fn build_fields(&self, builder: &mut FieldBuilder<'ctx>) -> Self::Fields { let llvm_i8 = builder.ctx.i8_type(); let llvm_i32 = builder.ctx.i32_type(); IrrtStringFields { @@ -157,10 +149,7 @@ impl<'ctx> StructureOptic<'ctx> for ErrorContextLens { "ErrorContext" } - fn build_fields( - &self, - builder: &mut crate::codegen::optics::FieldBuilder<'ctx>, - ) -> Self::Fields { + fn build_fields(&self, builder: &mut FieldBuilder<'ctx>) -> Self::Fields { ErrorContextFields { error_ids: builder.add_field("error_ids", AddressLens(ErrorIdsLens)), error_id: builder.add_field("error_id", IntLens(builder.ctx.i32_type())), diff --git a/nac3core/src/codegen/irrt/new.rs b/nac3core/src/codegen/irrt/new.rs index c529bff7..6fb90b18 100644 --- a/nac3core/src/codegen/irrt/new.rs +++ b/nac3core/src/codegen/irrt/new.rs @@ -4,7 +4,11 @@ use inkwell::{ }; use crate::codegen::{ - optics::{Address, AddressLens, IntLens, Optic, OpticValue, Prism}, + optics::{ + address::{Address, AddressLens}, + core::{Optic, OpticValue, Prism}, + int::IntLens, + }, CodeGenContext, CodeGenerator, }; use crate::util::SizeVariant; diff --git a/nac3core/src/codegen/irrt/numpy.rs b/nac3core/src/codegen/irrt/numpy.rs index ea576136..b4827f31 100644 --- a/nac3core/src/codegen/irrt/numpy.rs +++ b/nac3core/src/codegen/irrt/numpy.rs @@ -5,10 +5,10 @@ use inkwell::{ values::{BasicValueEnum, IntValue}, }; +use crate::codegen::optics::*; use crate::{ codegen::{ classes::{ListValue, UntypedArrayLikeAccessor}, - optics::{opaque_address_lens, Address, AddressLens, ArraySlice, IntLens, Ixed, Optic}, stmt::gen_for_callback_incrementing, CodeGenContext, CodeGenerator, }, diff --git a/nac3core/src/codegen/mod.rs b/nac3core/src/codegen/mod.rs index dd7c4310..b5d7a2d1 100644 --- a/nac3core/src/codegen/mod.rs +++ b/nac3core/src/codegen/mod.rs @@ -26,7 +26,7 @@ use inkwell::{ use irrt::classes::StrLens; use itertools::Itertools; use nac3parser::ast::{Location, Stmt, StrRef}; -use optics::Optic; +use optics::Optic as _; use parking_lot::{Condvar, Mutex}; use std::collections::{HashMap, HashSet}; use std::sync::{ diff --git a/nac3core/src/codegen/optics.rs b/nac3core/src/codegen/optics.rs deleted file mode 100644 index 97824b5c..00000000 --- a/nac3core/src/codegen/optics.rs +++ /dev/null @@ -1,449 +0,0 @@ -use inkwell::{ - context::Context, - types::{BasicType, BasicTypeEnum, IntType}, - values::{AnyValue, BasicValue, BasicValueEnum, IntValue, PointerValue, StructValue}, - AddressSpace, -}; -use itertools::Itertools; - -use super::{CodeGenContext, CodeGenerator}; - -// TODO: Write a taxonomy - -pub trait OpticValue<'ctx> { - fn get_llvm_value(&self) -> BasicValueEnum<'ctx>; -} - -impl<'ctx, T: BasicValue<'ctx>> OpticValue<'ctx> for T { - fn get_llvm_value(&self) -> BasicValueEnum<'ctx> { - self.as_basic_value_enum() - } -} - -// TODO: The interface is unintuitive -pub trait Optic<'ctx>: Clone { - type Value: OpticValue<'ctx>; - - fn get_llvm_type(&self, ctx: &'ctx Context) -> BasicTypeEnum<'ctx>; - - fn alloca(&self, ctx: &CodeGenContext<'ctx, '_>, name: &str) -> Address<'ctx, Self> { - let ptr = ctx.builder.build_alloca(self.get_llvm_type(ctx.ctx), name).unwrap(); - Address { addressee_optic: self.clone(), address: ptr } - } -} - -pub trait Prism<'ctx>: Optic<'ctx> { - // TODO: Return error if `review` fails - fn review>(&self, value: V) -> Self::Value; -} - -pub trait MemoryGetter<'ctx>: Optic<'ctx> { - fn get( - &self, - ctx: &CodeGenContext<'ctx, '_>, - pointer: PointerValue<'ctx>, - name: &str, - ) -> Self::Value; -} - -pub trait MemorySetter<'ctx>: Optic<'ctx> { - fn set(&self, ctx: &CodeGenContext<'ctx, '_>, pointer: PointerValue<'ctx>, value: &Self::Value); -} - -// NOTE: I wanted to make Int8Lens, Int16Lens, Int32Lens, with all -// having the trait IsIntLens, and implement `impl Optic for T`, -// but that clashes with StructureOptic!! -#[derive(Debug, Clone, Copy)] -pub struct IntLens<'ctx>(pub IntType<'ctx>); - -impl<'ctx> IntLens<'ctx> { - #[must_use] - pub fn int8(ctx: &'ctx Context) -> IntLens<'ctx> { - IntLens(ctx.i8_type()) - } - - #[must_use] - pub fn int32(ctx: &'ctx Context) -> IntLens<'ctx> { - IntLens(ctx.i32_type()) - } - - #[must_use] - pub fn int64(ctx: &'ctx Context) -> IntLens<'ctx> { - IntLens(ctx.i64_type()) - } -} - -impl<'ctx> Optic<'ctx> for IntLens<'ctx> { - type Value = IntValue<'ctx>; - - fn get_llvm_type(&self, _ctx: &'ctx Context) -> BasicTypeEnum<'ctx> { - self.0.as_basic_type_enum() - } -} - -impl<'ctx> Prism<'ctx> for IntLens<'ctx> { - fn review>(&self, value: V) -> Self::Value { - let int = value.as_any_value_enum().into_int_value(); - debug_assert_eq!(int.get_type().get_bit_width(), self.0.get_bit_width()); - int - } -} - -impl<'ctx> MemoryGetter<'ctx> for IntLens<'ctx> { - fn get( - &self, - ctx: &CodeGenContext<'ctx, '_>, - pointer: PointerValue<'ctx>, - name: &str, - ) -> Self::Value { - self.review(ctx.builder.build_load(pointer, name).unwrap()) - } -} - -impl<'ctx> MemorySetter<'ctx> for IntLens<'ctx> { - fn set(&self, ctx: &CodeGenContext<'ctx, '_>, pointer: PointerValue<'ctx>, int: &Self::Value) { - debug_assert_eq!(int.get_type().get_bit_width(), self.0.get_bit_width()); - ctx.builder.build_store(pointer, int.as_basic_value_enum()).unwrap(); - } -} - -#[derive(Debug, Clone)] -pub struct Address<'ctx, AddresseeOptic> { - pub addressee_optic: AddresseeOptic, - pub address: PointerValue<'ctx>, -} - -impl<'ctx, AddresseeOptic> Address<'ctx, AddresseeOptic> { - pub fn cast_to>( - &self, - ctx: &CodeGenContext<'ctx, '_>, - new_optic: S, - ) -> Address<'ctx, S> { - let to_ptr_type = new_optic.get_llvm_type(ctx.ctx).ptr_type(AddressSpace::default()); - let casted_address = - ctx.builder.build_pointer_cast(self.address, to_ptr_type, "ptr_casted").unwrap(); - Address { addressee_optic: new_optic, address: casted_address } - } - - pub fn cast_to_opaque(&self, ctx: &CodeGenContext<'ctx, '_>) -> Address<'ctx, IntLens<'ctx>> { - self.cast_to(ctx, IntLens::int8(ctx.ctx)) - } -} - -impl<'ctx, AddresseeOptic> OpticValue<'ctx> for Address<'ctx, AddresseeOptic> { - fn get_llvm_value(&self) -> BasicValueEnum<'ctx> { - self.address.as_basic_value_enum() - } -} - -#[derive(Debug, Clone)] -pub struct AddressLens(pub AddresseeOptic); - -#[must_use] -pub fn opaque_address_lens(ctx: &Context) -> AddressLens> { - AddressLens(IntLens::int8(ctx)) -} - -impl<'ctx, AddresseeOptic: Optic<'ctx>> Optic<'ctx> for AddressLens { - type Value = Address<'ctx, AddresseeOptic>; - - fn get_llvm_type(&self, ctx: &'ctx Context) -> BasicTypeEnum<'ctx> { - self.0.get_llvm_type(ctx).ptr_type(AddressSpace::default()).as_basic_type_enum() - } -} - -impl<'ctx, AddresseeOptic: Optic<'ctx>> Prism<'ctx> for AddressLens { - fn review>(&self, value: V) -> Self::Value { - Address { - addressee_optic: self.0.clone(), - address: value.as_any_value_enum().into_pointer_value(), - } - } -} - -impl<'ctx, AddressesOptic: Optic<'ctx>> MemoryGetter<'ctx> for AddressLens { - fn get( - &self, - ctx: &CodeGenContext<'ctx, '_>, - pointer: PointerValue<'ctx>, - name: &str, - ) -> Self::Value { - self.review(ctx.builder.build_load(pointer, name).unwrap()) - } -} - -impl<'ctx, AddressesOptic: Optic<'ctx>> MemorySetter<'ctx> for AddressLens { - fn set( - &self, - ctx: &CodeGenContext<'ctx, '_>, - pointer: PointerValue<'ctx>, - value: &Self::Value, - ) { - ctx.builder.build_store(pointer, value.address).unwrap(); - } -} - -// To make [`Address`] convenient to use -impl<'ctx, AddresseeOptic: MemoryGetter<'ctx>> Address<'ctx, AddresseeOptic> { - pub fn load(&self, ctx: &CodeGenContext<'ctx, '_>, name: &str) -> AddresseeOptic::Value { - self.addressee_optic.get(ctx, self.address, name) - } -} - -// To make [`Address`] convenient to use -impl<'ctx, AddresseeOptic: MemorySetter<'ctx>> Address<'ctx, AddresseeOptic> { - pub fn store(&self, ctx: &CodeGenContext<'ctx, '_>, value: &AddresseeOptic::Value) { - self.addressee_optic.set(ctx, self.address, value); - } -} - -// ((Memory, Pointer) -> ElementOptic::Value*) -#[derive(Debug, Clone)] -pub struct GepGetter { - /// The LLVM GEP index - pub gep_index: u64, - /// Element (or field in the context of `struct`s) name. Used for cosmetics. - pub name: &'static str, - /// The lens to view the actual value after applying this [`FieldLens`] - pub element_optic: ElementOptic, -} - -impl<'ctx, ElementOptic: Optic<'ctx>> Optic<'ctx> for GepGetter { - type Value = Address<'ctx, ElementOptic>; - - fn get_llvm_type(&self, ctx: &'ctx Context) -> BasicTypeEnum<'ctx> { - self.element_optic.get_llvm_type(ctx).ptr_type(AddressSpace::default()).as_basic_type_enum() - } -} - -impl<'ctx, ElementOptic: Optic<'ctx>> MemoryGetter<'ctx> for GepGetter { - fn get( - &self, - ctx: &CodeGenContext<'ctx, '_>, - pointer: PointerValue<'ctx>, - name: &str, - ) -> Self::Value { - let llvm_i32 = ctx.ctx.i32_type(); // TODO: I think I'm not supposed to *just* use i32 for GEP like that - let element_ptr = unsafe { - ctx.builder - .build_in_bounds_gep( - pointer, - &[llvm_i32.const_zero(), llvm_i32.const_int(self.gep_index, false)], - name, - ) - .unwrap() - }; - Address { address: element_ptr, addressee_optic: self.element_optic.clone() } - } -} - -// Only used by [`FieldBuilder`] -#[derive(Debug)] -struct FieldInfo<'ctx> { - gep_index: u64, - name: &'ctx str, - llvm_type: BasicTypeEnum<'ctx>, -} - -#[derive(Debug)] -pub struct FieldBuilder<'ctx> { - pub ctx: &'ctx Context, - gep_index_counter: u64, - struct_name: &'ctx str, - fields: Vec>, -} - -impl<'ctx> FieldBuilder<'ctx> { - #[must_use] - pub fn new(ctx: &'ctx Context, struct_name: &'ctx str) -> Self { - FieldBuilder { ctx, gep_index_counter: 0, struct_name, fields: Vec::new() } - } - - fn next_gep_index(&mut self) -> u64 { - let index = self.gep_index_counter; - self.gep_index_counter += 1; - index - } - - pub fn add_field>( - &mut self, - name: &'static str, - element_optic: ElementOptic, - ) -> GepGetter { - let gep_index = self.next_gep_index(); - - self.fields.push(FieldInfo { - gep_index, - name, - llvm_type: element_optic.get_llvm_type(self.ctx), - }); - - GepGetter { gep_index, name, element_optic } - } -} - -pub trait StructureOptic<'ctx>: Clone { - // Fields of optics - type Fields; - - // TODO: Make it an associated function instead? - fn struct_name(&self) -> &'static str; - - fn build_fields(&self, builder: &mut FieldBuilder<'ctx>) -> Self::Fields; - - fn get_fields(&self, ctx: &'ctx Context) -> Self::Fields { - let mut builder = FieldBuilder::new(ctx, self.struct_name()); - self.build_fields(&mut builder) - } -} - -pub struct OpticalStructValue<'ctx, StructOptic> { - optic: StructOptic, - llvm: StructValue<'ctx>, -} - -impl<'ctx, StructOptic> OpticValue<'ctx> for OpticalStructValue<'ctx, StructOptic> { - fn get_llvm_value(&self) -> BasicValueEnum<'ctx> { - self.llvm.as_basic_value_enum() - } -} - -// TODO: check StructType -impl<'ctx, T: StructureOptic<'ctx>> Optic<'ctx> for T { - type Value = OpticalStructValue<'ctx, Self>; - - fn get_llvm_type(&self, ctx: &'ctx Context) -> BasicTypeEnum<'ctx> { - let mut builder = FieldBuilder::new(ctx, self.struct_name()); - self.build_fields(&mut builder); // Self::Fields is discarded - - let field_types = - builder.fields.iter().map(|field_info| field_info.llvm_type).collect_vec(); - ctx.struct_type(&field_types, false).as_basic_type_enum() - } -} - -impl<'ctx, T: StructureOptic<'ctx>> MemoryGetter<'ctx> for T { - fn get( - &self, - ctx: &CodeGenContext<'ctx, '_>, - pointer: PointerValue<'ctx>, - name: &str, - ) -> Self::Value { - OpticalStructValue { - optic: self.clone(), - llvm: ctx.builder.build_load(pointer, name).unwrap().into_struct_value(), - } - } -} - -impl<'ctx, T: StructureOptic<'ctx>> MemorySetter<'ctx> for T { - fn set( - &self, - ctx: &CodeGenContext<'ctx, '_>, - pointer: PointerValue<'ctx>, - value: &Self::Value, - ) { - ctx.builder.build_store(pointer, value.llvm).unwrap(); - } -} - -impl<'ctx, AddresseeOptic: StructureOptic<'ctx>> Address<'ctx, AddresseeOptic> { - pub fn focus>( - &self, - ctx: &CodeGenContext<'ctx, '_>, - get_field_gep_fn: GetFieldGepFn, - ) -> Address<'ctx, FieldElementOptic> - where - GetFieldGepFn: FnOnce(&AddresseeOptic::Fields) -> &GepGetter, - { - let fields = self.addressee_optic.get_fields(ctx.ctx); - let field = get_field_gep_fn(&fields); - field.get(ctx, self.address, field.name) - } -} - -// Name inspired by https://hackage.haskell.org/package/lens-5.3.2/docs/Control-Lens-At.html#t:Ixed -pub trait Ixed<'ctx, ElementOptic> { - // TODO: Interface/Method to expose the IntType of index? - // or even make index itself parameterized? (probably no) - - fn ix( - &self, - ctx: &CodeGenContext<'ctx, '_>, - index: IntValue<'ctx>, - name: &str, - ) -> Address<'ctx, ElementOptic>; -} - -// TODO: Can do interface seggregation -pub trait BoundedIxed<'ctx, ElementOptic>: Ixed<'ctx, ElementOptic> { - fn num_elements(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx>; - - // Check if 0 <= index < self.num_elements() - fn ix_bounds_checked( - &self, - generator: &mut G, - ctx: &mut CodeGenContext<'ctx, '_>, - index: IntValue<'ctx>, - name: &str, - ) -> Address<'ctx, ElementOptic> { - let num_elements = self.num_elements(ctx); - let int_type = num_elements.get_type(); // NOTE: Weird get_type(), see comment under `trait Ixed` - - assert_eq!(int_type.get_bit_width(), index.get_type().get_bit_width()); // Might as well check bit width to catch bugs - - // TODO: SGE or UGE? or make it defined by the implementee? - - // Check `0 <= index` - let lower_bounded = ctx - .builder - .build_int_compare( - inkwell::IntPredicate::SLE, - int_type.const_zero(), - index, - "lower_bounded", - ) - .unwrap(); - - // Check `index < num_elements` - let upper_bounded = ctx - .builder - .build_int_compare(inkwell::IntPredicate::SLT, index, num_elements, "upper_bounded") - .unwrap(); - - // Compute `0 <= index && index < num_elements` - let bounded = ctx.builder.build_and(lower_bounded, upper_bounded, "bounded").unwrap(); - - // Assert `bounded` - ctx.make_assert(generator, bounded, "0:IndexError", "nac3core LLVM codegen attempting to access out of bounds array index {0}. Must satisfy 0 <= index < {2}", [Some(index), Some(num_elements), None], ctx.current_loc); - - // ...and finally do indexing - self.ix(ctx, index, name) - } -} - -pub struct ArraySlice<'ctx, ElementOptic> { - pub num_elements: IntValue<'ctx>, - pub base: Address<'ctx, ElementOptic>, -} - -impl<'ctx, ElementOptic: Optic<'ctx>> Ixed<'ctx, ElementOptic> for ArraySlice<'ctx, ElementOptic> { - fn ix( - &self, - ctx: &CodeGenContext<'ctx, '_>, - index: IntValue<'ctx>, - name: &str, - ) -> Address<'ctx, ElementOptic> { - let element_addr = - unsafe { ctx.builder.build_in_bounds_gep(self.base.address, &[index], name).unwrap() }; - Address { address: element_addr, addressee_optic: self.base.addressee_optic.clone() } - } -} - -impl<'ctx, ElementOptic: Optic<'ctx>> BoundedIxed<'ctx, ElementOptic> - for ArraySlice<'ctx, ElementOptic> -{ - fn num_elements(&self, _ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { - self.num_elements - } -} diff --git a/nac3core/src/codegen/optics/address.rs b/nac3core/src/codegen/optics/address.rs new file mode 100644 index 00000000..c27ce20d --- /dev/null +++ b/nac3core/src/codegen/optics/address.rs @@ -0,0 +1,103 @@ +use inkwell::{ + context::Context, + types::{BasicType, BasicTypeEnum}, + values::{AnyValue, BasicValue, BasicValueEnum, PointerValue}, + AddressSpace, +}; + +use crate::codegen::CodeGenContext; + +use super::{ + core::{MemoryGetter, MemorySetter, Optic, OpticValue, Prism}, + int::IntLens, +}; + +#[derive(Debug, Clone)] +pub struct Address<'ctx, AddresseeOptic> { + pub addressee_optic: AddresseeOptic, + pub address: PointerValue<'ctx>, +} + +impl<'ctx, AddresseeOptic> Address<'ctx, AddresseeOptic> { + pub fn cast_to>( + &self, + ctx: &CodeGenContext<'ctx, '_>, + new_optic: S, + ) -> Address<'ctx, S> { + let to_ptr_type = new_optic.get_llvm_type(ctx.ctx).ptr_type(AddressSpace::default()); + let casted_address = + ctx.builder.build_pointer_cast(self.address, to_ptr_type, "ptr_casted").unwrap(); + Address { addressee_optic: new_optic, address: casted_address } + } + + pub fn cast_to_opaque(&self, ctx: &CodeGenContext<'ctx, '_>) -> Address<'ctx, IntLens<'ctx>> { + self.cast_to(ctx, IntLens::int8(ctx.ctx)) + } +} + +impl<'ctx, AddresseeOptic> OpticValue<'ctx> for Address<'ctx, AddresseeOptic> { + fn get_llvm_value(&self) -> BasicValueEnum<'ctx> { + self.address.as_basic_value_enum() + } +} + +#[derive(Debug, Clone)] +pub struct AddressLens(pub AddresseeOptic); + +#[must_use] +pub fn opaque_address_lens(ctx: &Context) -> AddressLens> { + AddressLens(IntLens::int8(ctx)) +} + +impl<'ctx, AddresseeOptic: Optic<'ctx>> Optic<'ctx> for AddressLens { + type Value = Address<'ctx, AddresseeOptic>; + + fn get_llvm_type(&self, ctx: &'ctx Context) -> BasicTypeEnum<'ctx> { + self.0.get_llvm_type(ctx).ptr_type(AddressSpace::default()).as_basic_type_enum() + } +} + +impl<'ctx, AddresseeOptic: Optic<'ctx>> Prism<'ctx> for AddressLens { + fn review>(&self, value: V) -> Self::Value { + Address { + addressee_optic: self.0.clone(), + address: value.as_any_value_enum().into_pointer_value(), + } + } +} + +impl<'ctx, AddressesOptic: Optic<'ctx>> MemoryGetter<'ctx> for AddressLens { + fn get( + &self, + ctx: &CodeGenContext<'ctx, '_>, + pointer: PointerValue<'ctx>, + name: &str, + ) -> Self::Value { + self.review(ctx.builder.build_load(pointer, name).unwrap()) + } +} + +impl<'ctx, AddressesOptic: Optic<'ctx>> MemorySetter<'ctx> for AddressLens { + fn set( + &self, + ctx: &CodeGenContext<'ctx, '_>, + pointer: PointerValue<'ctx>, + value: &Self::Value, + ) { + ctx.builder.build_store(pointer, value.address).unwrap(); + } +} + +// To make [`Address`] convenient to use +impl<'ctx, AddresseeOptic: MemoryGetter<'ctx>> Address<'ctx, AddresseeOptic> { + pub fn load(&self, ctx: &CodeGenContext<'ctx, '_>, name: &str) -> AddresseeOptic::Value { + self.addressee_optic.get(ctx, self.address, name) + } +} + +// To make [`Address`] convenient to use +impl<'ctx, AddresseeOptic: MemorySetter<'ctx>> Address<'ctx, AddresseeOptic> { + pub fn store(&self, ctx: &CodeGenContext<'ctx, '_>, value: &AddresseeOptic::Value) { + self.addressee_optic.set(ctx, self.address, value); + } +} diff --git a/nac3core/src/codegen/optics/core.rs b/nac3core/src/codegen/optics/core.rs new file mode 100644 index 00000000..1b3e1794 --- /dev/null +++ b/nac3core/src/codegen/optics/core.rs @@ -0,0 +1,51 @@ +use inkwell::{ + context::Context, + types::BasicTypeEnum, + values::{AnyValue, BasicValue, BasicValueEnum, PointerValue}, +}; + +use crate::codegen::CodeGenContext; + +use super::address::Address; + +// TODO: Write a taxonomy + +pub trait OpticValue<'ctx> { + fn get_llvm_value(&self) -> BasicValueEnum<'ctx>; +} + +impl<'ctx, T: BasicValue<'ctx>> OpticValue<'ctx> for T { + fn get_llvm_value(&self) -> BasicValueEnum<'ctx> { + self.as_basic_value_enum() + } +} + +// TODO: The interface is unintuitive +pub trait Optic<'ctx>: Clone { + type Value: OpticValue<'ctx>; + + fn get_llvm_type(&self, ctx: &'ctx Context) -> BasicTypeEnum<'ctx>; + + fn alloca(&self, ctx: &CodeGenContext<'ctx, '_>, name: &str) -> Address<'ctx, Self> { + let ptr = ctx.builder.build_alloca(self.get_llvm_type(ctx.ctx), name).unwrap(); + Address { addressee_optic: self.clone(), address: ptr } + } +} + +pub trait Prism<'ctx>: Optic<'ctx> { + // TODO: Return error if `review` fails + fn review>(&self, value: V) -> Self::Value; +} + +pub trait MemoryGetter<'ctx>: Optic<'ctx> { + fn get( + &self, + ctx: &CodeGenContext<'ctx, '_>, + pointer: PointerValue<'ctx>, + name: &str, + ) -> Self::Value; +} + +pub trait MemorySetter<'ctx>: Optic<'ctx> { + fn set(&self, ctx: &CodeGenContext<'ctx, '_>, pointer: PointerValue<'ctx>, value: &Self::Value); +} diff --git a/nac3core/src/codegen/optics/gep.rs b/nac3core/src/codegen/optics/gep.rs new file mode 100644 index 00000000..b6633870 --- /dev/null +++ b/nac3core/src/codegen/optics/gep.rs @@ -0,0 +1,53 @@ +use inkwell::{ + context::Context, + types::{BasicType, BasicTypeEnum}, + values::PointerValue, + AddressSpace, +}; + +use crate::codegen::CodeGenContext; + +use super::{ + address::Address, + core::{MemoryGetter, Optic}, +}; + +// ((Memory, Pointer) -> ElementOptic::Value*) +#[derive(Debug, Clone)] +pub struct GepGetter { + /// The LLVM GEP index + pub gep_index: u64, + /// Element (or field in the context of `struct`s) name. Used for cosmetics. + pub name: &'static str, + /// The lens to view the actual value after applying this [`FieldLens`] + pub element_optic: ElementOptic, +} + +impl<'ctx, ElementOptic: Optic<'ctx>> Optic<'ctx> for GepGetter { + type Value = Address<'ctx, ElementOptic>; + + fn get_llvm_type(&self, ctx: &'ctx Context) -> BasicTypeEnum<'ctx> { + self.element_optic.get_llvm_type(ctx).ptr_type(AddressSpace::default()).as_basic_type_enum() + } +} + +impl<'ctx, ElementOptic: Optic<'ctx>> MemoryGetter<'ctx> for GepGetter { + fn get( + &self, + ctx: &CodeGenContext<'ctx, '_>, + pointer: PointerValue<'ctx>, + name: &str, + ) -> Self::Value { + let llvm_i32 = ctx.ctx.i32_type(); // TODO: I think I'm not supposed to *just* use i32 for GEP like that + let element_ptr = unsafe { + ctx.builder + .build_in_bounds_gep( + pointer, + &[llvm_i32.const_zero(), llvm_i32.const_int(self.gep_index, false)], + name, + ) + .unwrap() + }; + Address { address: element_ptr, addressee_optic: self.element_optic.clone() } + } +} diff --git a/nac3core/src/codegen/optics/int.rs b/nac3core/src/codegen/optics/int.rs new file mode 100644 index 00000000..852fbf0d --- /dev/null +++ b/nac3core/src/codegen/optics/int.rs @@ -0,0 +1,66 @@ +use inkwell::{ + context::Context, + types::{BasicType, BasicTypeEnum, IntType}, + values::{AnyValue, BasicValue, IntValue, PointerValue}, +}; + +use crate::codegen::CodeGenContext; + +use super::core::{MemoryGetter, MemorySetter, Optic, Prism}; + +// NOTE: I wanted to make Int8Lens, Int16Lens, Int32Lens, with all +// having the trait IsIntLens, and implement `impl Optic for T`, +// but that clashes with StructureOptic!! +#[derive(Debug, Clone, Copy)] +pub struct IntLens<'ctx>(pub IntType<'ctx>); + +impl<'ctx> IntLens<'ctx> { + #[must_use] + pub fn int8(ctx: &'ctx Context) -> IntLens<'ctx> { + IntLens(ctx.i8_type()) + } + + #[must_use] + pub fn int32(ctx: &'ctx Context) -> IntLens<'ctx> { + IntLens(ctx.i32_type()) + } + + #[must_use] + pub fn int64(ctx: &'ctx Context) -> IntLens<'ctx> { + IntLens(ctx.i64_type()) + } +} + +impl<'ctx> Optic<'ctx> for IntLens<'ctx> { + type Value = IntValue<'ctx>; + + fn get_llvm_type(&self, _ctx: &'ctx Context) -> BasicTypeEnum<'ctx> { + self.0.as_basic_type_enum() + } +} + +impl<'ctx> Prism<'ctx> for IntLens<'ctx> { + fn review>(&self, value: V) -> Self::Value { + let int = value.as_any_value_enum().into_int_value(); + debug_assert_eq!(int.get_type().get_bit_width(), self.0.get_bit_width()); + int + } +} + +impl<'ctx> MemoryGetter<'ctx> for IntLens<'ctx> { + fn get( + &self, + ctx: &CodeGenContext<'ctx, '_>, + pointer: PointerValue<'ctx>, + name: &str, + ) -> Self::Value { + self.review(ctx.builder.build_load(pointer, name).unwrap()) + } +} + +impl<'ctx> MemorySetter<'ctx> for IntLens<'ctx> { + fn set(&self, ctx: &CodeGenContext<'ctx, '_>, pointer: PointerValue<'ctx>, int: &Self::Value) { + debug_assert_eq!(int.get_type().get_bit_width(), self.0.get_bit_width()); + ctx.builder.build_store(pointer, int.as_basic_value_enum()).unwrap(); + } +} diff --git a/nac3core/src/codegen/optics/ixed.rs b/nac3core/src/codegen/optics/ixed.rs new file mode 100644 index 00000000..0c0bff87 --- /dev/null +++ b/nac3core/src/codegen/optics/ixed.rs @@ -0,0 +1,65 @@ +use inkwell::values::IntValue; + +use crate::codegen::{CodeGenContext, CodeGenerator}; + +use super::address::Address; + +// Name inspired by https://hackage.haskell.org/package/lens-5.3.2/docs/Control-Lens-At.html#t:Ixed +pub trait Ixed<'ctx, ElementOptic> { + // TODO: Interface/Method to expose the IntType of index? + // or even make index itself parameterized? (probably no) + + fn ix( + &self, + ctx: &CodeGenContext<'ctx, '_>, + index: IntValue<'ctx>, + name: &str, + ) -> Address<'ctx, ElementOptic>; +} + +// TODO: Can do interface seggregation +pub trait BoundedIxed<'ctx, ElementOptic>: Ixed<'ctx, ElementOptic> { + fn num_elements(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx>; + + // Check if 0 <= index < self.num_elements() + fn ix_bounds_checked( + &self, + generator: &mut G, + ctx: &mut CodeGenContext<'ctx, '_>, + index: IntValue<'ctx>, + name: &str, + ) -> Address<'ctx, ElementOptic> { + let num_elements = self.num_elements(ctx); + let int_type = num_elements.get_type(); // NOTE: Weird get_type(), see comment under `trait Ixed` + + assert_eq!(int_type.get_bit_width(), index.get_type().get_bit_width()); // Might as well check bit width to catch bugs + + // TODO: SGE or UGE? or make it defined by the implementee? + + // Check `0 <= index` + let lower_bounded = ctx + .builder + .build_int_compare( + inkwell::IntPredicate::SLE, + int_type.const_zero(), + index, + "lower_bounded", + ) + .unwrap(); + + // Check `index < num_elements` + let upper_bounded = ctx + .builder + .build_int_compare(inkwell::IntPredicate::SLT, index, num_elements, "upper_bounded") + .unwrap(); + + // Compute `0 <= index && index < num_elements` + let bounded = ctx.builder.build_and(lower_bounded, upper_bounded, "bounded").unwrap(); + + // Assert `bounded` + ctx.make_assert(generator, bounded, "0:IndexError", "nac3core LLVM codegen attempting to access out of bounds array index {0}. Must satisfy 0 <= index < {2}", [Some(index), Some(num_elements), None], ctx.current_loc); + + // ...and finally do indexing + self.ix(ctx, index, name) + } +} diff --git a/nac3core/src/codegen/optics/mod.rs b/nac3core/src/codegen/optics/mod.rs new file mode 100644 index 00000000..df49c546 --- /dev/null +++ b/nac3core/src/codegen/optics/mod.rs @@ -0,0 +1,15 @@ +pub mod address; +pub mod core; +pub mod gep; +pub mod int; +pub mod ixed; +pub mod slice; +pub mod structure; + +pub use address::*; +pub use core::*; +pub use gep::*; +pub use int::*; +pub use ixed::*; +pub use slice::*; +pub use structure::*; diff --git a/nac3core/src/codegen/optics/slice.rs b/nac3core/src/codegen/optics/slice.rs new file mode 100644 index 00000000..8f1f4d60 --- /dev/null +++ b/nac3core/src/codegen/optics/slice.rs @@ -0,0 +1,36 @@ +use super::{ + core::Optic, + ixed::{BoundedIxed, Ixed}, +}; + +use inkwell::values::IntValue; + +use crate::codegen::CodeGenContext; + +use super::address::Address; + +pub struct ArraySlice<'ctx, ElementOptic> { + pub num_elements: IntValue<'ctx>, + pub base: Address<'ctx, ElementOptic>, +} + +impl<'ctx, ElementOptic: Optic<'ctx>> Ixed<'ctx, ElementOptic> for ArraySlice<'ctx, ElementOptic> { + fn ix( + &self, + ctx: &CodeGenContext<'ctx, '_>, + index: IntValue<'ctx>, + name: &str, + ) -> Address<'ctx, ElementOptic> { + let element_addr = + unsafe { ctx.builder.build_in_bounds_gep(self.base.address, &[index], name).unwrap() }; + Address { address: element_addr, addressee_optic: self.base.addressee_optic.clone() } + } +} + +impl<'ctx, ElementOptic: Optic<'ctx>> BoundedIxed<'ctx, ElementOptic> + for ArraySlice<'ctx, ElementOptic> +{ + fn num_elements(&self, _ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> { + self.num_elements + } +} diff --git a/nac3core/src/codegen/optics/structure.rs b/nac3core/src/codegen/optics/structure.rs new file mode 100644 index 00000000..a1e334f9 --- /dev/null +++ b/nac3core/src/codegen/optics/structure.rs @@ -0,0 +1,139 @@ +use inkwell::{ + context::Context, + types::{BasicType, BasicTypeEnum}, + values::{BasicValue, BasicValueEnum, PointerValue, StructValue}, +}; +use itertools::Itertools; + +use crate::codegen::CodeGenContext; + +use super::{ + address::Address, + core::{MemoryGetter, MemorySetter, Optic, OpticValue}, + gep::GepGetter, +}; + +pub trait StructureOptic<'ctx>: Clone { + // Fields of optics + type Fields; + + // TODO: Make it an associated function instead? + fn struct_name(&self) -> &'static str; + + fn build_fields(&self, builder: &mut FieldBuilder<'ctx>) -> Self::Fields; + + fn get_fields(&self, ctx: &'ctx Context) -> Self::Fields { + let mut builder = FieldBuilder::new(ctx, self.struct_name()); + self.build_fields(&mut builder) + } +} + +pub struct OpticalStructValue<'ctx, StructOptic> { + optic: StructOptic, + llvm: StructValue<'ctx>, +} + +impl<'ctx, StructOptic> OpticValue<'ctx> for OpticalStructValue<'ctx, StructOptic> { + fn get_llvm_value(&self) -> BasicValueEnum<'ctx> { + self.llvm.as_basic_value_enum() + } +} + +// TODO: check StructType +impl<'ctx, T: StructureOptic<'ctx>> Optic<'ctx> for T { + type Value = OpticalStructValue<'ctx, Self>; + + fn get_llvm_type(&self, ctx: &'ctx Context) -> BasicTypeEnum<'ctx> { + let mut builder = FieldBuilder::new(ctx, self.struct_name()); + self.build_fields(&mut builder); // Self::Fields is discarded + + let field_types = + builder.fields.iter().map(|field_info| field_info.llvm_type).collect_vec(); + ctx.struct_type(&field_types, false).as_basic_type_enum() + } +} + +impl<'ctx, T: StructureOptic<'ctx>> MemoryGetter<'ctx> for T { + fn get( + &self, + ctx: &CodeGenContext<'ctx, '_>, + pointer: PointerValue<'ctx>, + name: &str, + ) -> Self::Value { + OpticalStructValue { + optic: self.clone(), + llvm: ctx.builder.build_load(pointer, name).unwrap().into_struct_value(), + } + } +} + +impl<'ctx, T: StructureOptic<'ctx>> MemorySetter<'ctx> for T { + fn set( + &self, + ctx: &CodeGenContext<'ctx, '_>, + pointer: PointerValue<'ctx>, + value: &Self::Value, + ) { + ctx.builder.build_store(pointer, value.llvm).unwrap(); + } +} + +impl<'ctx, AddresseeOptic: StructureOptic<'ctx>> Address<'ctx, AddresseeOptic> { + pub fn focus>( + &self, + ctx: &CodeGenContext<'ctx, '_>, + get_field_gep_fn: GetFieldGepFn, + ) -> Address<'ctx, FieldElementOptic> + where + GetFieldGepFn: FnOnce(&AddresseeOptic::Fields) -> &GepGetter, + { + let fields = self.addressee_optic.get_fields(ctx.ctx); + let field = get_field_gep_fn(&fields); + field.get(ctx, self.address, field.name) + } +} + +// Only used by [`FieldBuilder`] +#[derive(Debug)] +struct FieldInfo<'ctx> { + gep_index: u64, + name: &'ctx str, + llvm_type: BasicTypeEnum<'ctx>, +} + +#[derive(Debug)] +pub struct FieldBuilder<'ctx> { + pub ctx: &'ctx Context, + gep_index_counter: u64, + struct_name: &'ctx str, + fields: Vec>, +} + +impl<'ctx> FieldBuilder<'ctx> { + #[must_use] + pub fn new(ctx: &'ctx Context, struct_name: &'ctx str) -> Self { + FieldBuilder { ctx, gep_index_counter: 0, struct_name, fields: Vec::new() } + } + + fn next_gep_index(&mut self) -> u64 { + let index = self.gep_index_counter; + self.gep_index_counter += 1; + index + } + + pub fn add_field>( + &mut self, + name: &'static str, + element_optic: ElementOptic, + ) -> GepGetter { + let gep_index = self.next_gep_index(); + + self.fields.push(FieldInfo { + gep_index, + name, + llvm_type: element_optic.get_llvm_type(self.ctx), + }); + + GepGetter { gep_index, name, element_optic } + } +}