core/model: introduce codegen/model
This commit is contained in:
parent
c1369ea5bd
commit
c772fdb83a
@ -41,6 +41,7 @@ pub mod extern_fns;
|
||||
mod generator;
|
||||
pub mod irrt;
|
||||
pub mod llvm_intrinsics;
|
||||
pub mod model;
|
||||
pub mod numpy;
|
||||
pub mod stmt;
|
||||
|
||||
|
161
nac3core/src/codegen/model/core.rs
Normal file
161
nac3core/src/codegen/model/core.rs
Normal file
@ -0,0 +1,161 @@
|
||||
use std::fmt;
|
||||
|
||||
use inkwell::{context::Context, types::*, values::*};
|
||||
|
||||
use super::*;
|
||||
use crate::codegen::{CodeGenContext, CodeGenerator};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct TypeContext<'ctx> {
|
||||
pub size_type: IntType<'ctx>,
|
||||
}
|
||||
|
||||
pub trait HasTypeContext {
|
||||
fn type_context<'ctx>(&self, ctx: &'ctx Context) -> TypeContext<'ctx>;
|
||||
}
|
||||
|
||||
impl<T: CodeGenerator + ?Sized> HasTypeContext for T {
|
||||
fn type_context<'ctx>(&self, ctx: &'ctx Context) -> TypeContext<'ctx> {
|
||||
TypeContext { size_type: self.get_size_type(ctx) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ModelError(pub String);
|
||||
|
||||
impl ModelError {
|
||||
pub(super) fn under_context(mut self, context: &str) -> Self {
|
||||
self.0.push_str(" ... in ");
|
||||
self.0.push_str(context);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`Model`] is a singleton object that uniquely identifies a [`BasicType`]
|
||||
/// solely from a [`CodeGenerator`] and a [`Context`].
|
||||
pub trait Model: CheckType + fmt::Debug + Clone + Copy + Default {
|
||||
type Value<'ctx>: BasicValue<'ctx> + TryFrom<BasicValueEnum<'ctx>>;
|
||||
type Type<'ctx>: BasicType<'ctx>;
|
||||
|
||||
/// Return the [`BasicType`] of this model.
|
||||
fn get_type<'ctx>(&self, tyctx: TypeContext<'ctx>, ctx: &'ctx Context) -> Self::Type<'ctx>;
|
||||
|
||||
/// Check if a [`BasicType`] is the same type of this model.
|
||||
fn check_type<'ctx, T: BasicType<'ctx>>(
|
||||
&self,
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &'ctx Context,
|
||||
ty: T,
|
||||
) -> Result<(), ModelError> {
|
||||
let ty = ty.as_basic_type_enum();
|
||||
self.check_type_impl(tyctx, ctx, ty.as_basic_type_enum())
|
||||
}
|
||||
|
||||
/// Create an instance from a value with [`Instance::model`] being this model.
|
||||
///
|
||||
/// Caller must make sure the type of `value` and the type of this `model` are equivalent.
|
||||
fn believe_value<'ctx>(&self, value: Self::Value<'ctx>) -> Instance<'ctx, Self> {
|
||||
Instance { model: *self, value }
|
||||
}
|
||||
|
||||
/// Check if a [`BasicValue`]'s type is equivalent to the type of this model.
|
||||
/// Wrap it into an [`Instance`] if it is.
|
||||
fn check_value<'ctx, V: BasicValue<'ctx>>(
|
||||
&self,
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &'ctx Context,
|
||||
value: V,
|
||||
) -> Result<Instance<'ctx, Self>, ModelError> {
|
||||
let value = value.as_basic_value_enum();
|
||||
self.check_type(tyctx, ctx, value.get_type())
|
||||
.map_err(|err| err.under_context("the value {value:?}"))?;
|
||||
|
||||
let Ok(value) = Self::Value::try_from(value) else {
|
||||
unreachable!("check_type() has bad implementation")
|
||||
};
|
||||
Ok(self.believe_value(value))
|
||||
}
|
||||
|
||||
// Allocate a value on the stack and return its pointer.
|
||||
fn alloca<'ctx>(
|
||||
&self,
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
name: &str,
|
||||
) -> Ptr<'ctx, Self> {
|
||||
let pmodel = PtrModel(*self);
|
||||
let p = ctx.builder.build_alloca(self.get_type(tyctx, ctx.ctx), name).unwrap();
|
||||
pmodel.believe_value(p)
|
||||
}
|
||||
|
||||
// Allocate an array on the stack and return its pointer.
|
||||
fn array_alloca<'ctx>(
|
||||
&self,
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
len: IntValue<'ctx>,
|
||||
name: &str,
|
||||
) -> Ptr<'ctx, Self> {
|
||||
let pmodel = PtrModel(*self);
|
||||
let p = ctx.builder.build_array_alloca(self.get_type(tyctx, ctx.ctx), len, name).unwrap();
|
||||
pmodel.believe_value(p)
|
||||
}
|
||||
|
||||
fn var_alloca<'ctx, G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
name: Option<&str>,
|
||||
) -> Result<Ptr<'ctx, Self>, String> {
|
||||
let tyctx = generator.type_context(ctx.ctx);
|
||||
|
||||
let pmodel = PtrModel(*self);
|
||||
let p = generator.gen_var_alloc(
|
||||
ctx,
|
||||
self.get_type(tyctx, ctx.ctx).as_basic_type_enum(),
|
||||
name,
|
||||
)?;
|
||||
Ok(pmodel.believe_value(p))
|
||||
}
|
||||
|
||||
fn array_var_alloca<'ctx, G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
len: IntValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> Result<Ptr<'ctx, Self>, String> {
|
||||
let tyctx = generator.type_context(ctx.ctx);
|
||||
|
||||
// TODO: Remove ArraySliceValue
|
||||
let pmodel = PtrModel(*self);
|
||||
let p = generator.gen_array_var_alloc(
|
||||
ctx,
|
||||
self.get_type(tyctx, ctx.ctx).as_basic_type_enum(),
|
||||
len,
|
||||
name,
|
||||
)?;
|
||||
Ok(pmodel.believe_value(PointerValue::from(p)))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Instance<'ctx, M: Model> {
|
||||
/// The model of this instance.
|
||||
pub model: M,
|
||||
/// The value of this instance.
|
||||
///
|
||||
/// Caller must make sure the type of `value` and the type of this `model` are equivalent,
|
||||
/// down to having the same [`IntType::get_bit_width`] in case of [`IntType`] for example.
|
||||
pub value: M::Value<'ctx>,
|
||||
}
|
||||
|
||||
// NOTE: Must be Rust object-safe - This must be typeable for a Rust trait object.
|
||||
pub trait CheckType {
|
||||
fn check_type_impl<'ctx>(
|
||||
&self,
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &'ctx Context,
|
||||
ty: BasicTypeEnum<'ctx>,
|
||||
) -> Result<(), ModelError>;
|
||||
}
|
228
nac3core/src/codegen/model/int.rs
Normal file
228
nac3core/src/codegen/model/int.rs
Normal file
@ -0,0 +1,228 @@
|
||||
use std::fmt;
|
||||
|
||||
use inkwell::{
|
||||
context::Context,
|
||||
types::{BasicTypeEnum, IntType},
|
||||
values::IntValue,
|
||||
IntPredicate,
|
||||
};
|
||||
|
||||
use crate::codegen::{CodeGenContext, CodeGenerator};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub trait IntKind: fmt::Debug + Clone + Copy + Default {
|
||||
fn get_int_type<'ctx>(&self, tyctx: TypeContext<'ctx>, 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 IntKind for Bool {
|
||||
fn get_int_type<'ctx>(&self, _tyctx: TypeContext<'ctx>, ctx: &'ctx Context) -> IntType<'ctx> {
|
||||
ctx.bool_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntKind for Byte {
|
||||
fn get_int_type<'ctx>(&self, _tyctx: TypeContext<'ctx>, ctx: &'ctx Context) -> IntType<'ctx> {
|
||||
ctx.i8_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntKind for Int32 {
|
||||
fn get_int_type<'ctx>(&self, _tyctx: TypeContext<'ctx>, ctx: &'ctx Context) -> IntType<'ctx> {
|
||||
ctx.i32_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntKind for Int64 {
|
||||
fn get_int_type<'ctx>(&self, _tyctx: TypeContext<'ctx>, ctx: &'ctx Context) -> IntType<'ctx> {
|
||||
ctx.i64_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntKind for SizeT {
|
||||
fn get_int_type<'ctx>(&self, tyctx: TypeContext<'ctx>, _ctx: &'ctx Context) -> IntType<'ctx> {
|
||||
tyctx.size_type
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct IntModel<N: IntKind>(pub N);
|
||||
pub type Int<'ctx, N> = Instance<'ctx, IntModel<N>>;
|
||||
|
||||
impl<N: IntKind> CheckType for IntModel<N> {
|
||||
fn check_type_impl<'ctx>(
|
||||
&self,
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &'ctx Context,
|
||||
ty: BasicTypeEnum<'ctx>,
|
||||
) -> Result<(), ModelError> {
|
||||
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(tyctx, 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<N: IntKind> Model for IntModel<N> {
|
||||
type Value<'ctx> = IntValue<'ctx>;
|
||||
type Type<'ctx> = IntType<'ctx>;
|
||||
|
||||
#[must_use]
|
||||
fn get_type<'ctx>(&self, tyctx: TypeContext<'ctx>, ctx: &'ctx Context) -> Self::Type<'ctx> {
|
||||
self.0.get_int_type(tyctx, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: IntKind> IntModel<N> {
|
||||
pub fn constant<'ctx>(
|
||||
&self,
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &'ctx Context,
|
||||
value: u64,
|
||||
) -> Int<'ctx, N> {
|
||||
let value = self.get_type(tyctx, ctx).const_int(value, false);
|
||||
self.believe_value(value)
|
||||
}
|
||||
|
||||
pub fn const_0<'ctx>(&self, tyctx: TypeContext<'ctx>, ctx: &'ctx Context) -> Int<'ctx, N> {
|
||||
self.constant(tyctx, ctx, 0)
|
||||
}
|
||||
|
||||
pub fn const_1<'ctx>(&self, tyctx: TypeContext<'ctx>, ctx: &'ctx Context) -> Int<'ctx, N> {
|
||||
self.constant(tyctx, ctx, 1)
|
||||
}
|
||||
|
||||
pub fn s_extend_or_bit_cast<'ctx>(
|
||||
&self,
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
value: IntValue<'ctx>,
|
||||
name: &str,
|
||||
) -> Int<'ctx, N> {
|
||||
let value = ctx
|
||||
.builder
|
||||
.build_int_s_extend_or_bit_cast(value, self.get_type(tyctx, ctx.ctx), name)
|
||||
.unwrap();
|
||||
self.believe_value(value)
|
||||
}
|
||||
|
||||
pub fn truncate<'ctx>(
|
||||
&self,
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
value: IntValue<'ctx>,
|
||||
name: &str,
|
||||
) -> Int<'ctx, N> {
|
||||
let value =
|
||||
ctx.builder.build_int_truncate(value, self.get_type(tyctx, ctx.ctx), name).unwrap();
|
||||
self.believe_value(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntModel<Bool> {
|
||||
#[must_use]
|
||||
pub fn const_false<'ctx>(
|
||||
&self,
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &'ctx Context,
|
||||
) -> Int<'ctx, Bool> {
|
||||
self.constant(tyctx, ctx, 0)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn const_true<'ctx>(
|
||||
&self,
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &'ctx Context,
|
||||
) -> Int<'ctx, Bool> {
|
||||
self.constant(tyctx, ctx, 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx, N: IntKind> Int<'ctx, N> {
|
||||
pub fn s_extend_or_bit_cast<NewN: IntKind, G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
to_int_kind: NewN,
|
||||
name: &str,
|
||||
) -> Int<'ctx, NewN> {
|
||||
IntModel(to_int_kind).s_extend_or_bit_cast(tyctx, ctx, self.value, name)
|
||||
}
|
||||
|
||||
pub fn truncate<NewN: IntKind, G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
to_int_kind: NewN,
|
||||
name: &str,
|
||||
) -> Int<'ctx, NewN> {
|
||||
IntModel(to_int_kind).truncate(tyctx, ctx, self.value, name)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn add<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
other: Int<'ctx, N>,
|
||||
name: &str,
|
||||
) -> Int<'ctx, N> {
|
||||
let value = ctx.builder.build_int_add(self.value, other.value, name).unwrap();
|
||||
self.model.believe_value(value)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn sub<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
other: Int<'ctx, N>,
|
||||
name: &str,
|
||||
) -> Int<'ctx, N> {
|
||||
let value = ctx.builder.build_int_sub(self.value, other.value, name).unwrap();
|
||||
self.model.believe_value(value)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn mul<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
other: Int<'ctx, N>,
|
||||
name: &str,
|
||||
) -> Int<'ctx, N> {
|
||||
let value = ctx.builder.build_int_mul(self.value, other.value, name).unwrap();
|
||||
self.model.believe_value(value)
|
||||
}
|
||||
|
||||
pub fn compare<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
op: IntPredicate,
|
||||
other: Int<'ctx, N>,
|
||||
name: &str,
|
||||
) -> Int<'ctx, Bool> {
|
||||
let bool_model = IntModel(Bool);
|
||||
let value = ctx.builder.build_int_compare(op, self.value, other.value, name).unwrap();
|
||||
bool_model.believe_value(value)
|
||||
}
|
||||
}
|
11
nac3core/src/codegen/model/mod.rs
Normal file
11
nac3core/src/codegen/model/mod.rs
Normal file
@ -0,0 +1,11 @@
|
||||
mod core;
|
||||
mod int;
|
||||
mod ptr;
|
||||
mod slice;
|
||||
mod structure;
|
||||
|
||||
pub use core::*;
|
||||
pub use int::*;
|
||||
pub use ptr::*;
|
||||
pub use slice::*;
|
||||
pub use structure::*;
|
141
nac3core/src/codegen/model/ptr.rs
Normal file
141
nac3core/src/codegen/model/ptr.rs
Normal file
@ -0,0 +1,141 @@
|
||||
use inkwell::{
|
||||
context::Context,
|
||||
types::{BasicType, BasicTypeEnum, PointerType},
|
||||
values::{IntValue, PointerValue},
|
||||
AddressSpace,
|
||||
};
|
||||
|
||||
use crate::codegen::CodeGenContext;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct PtrModel<Element>(pub Element);
|
||||
pub type Ptr<'ctx, Element> = Instance<'ctx, PtrModel<Element>>;
|
||||
|
||||
impl<Element: CheckType> CheckType for PtrModel<Element> {
|
||||
fn check_type_impl<'ctx>(
|
||||
&self,
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &'ctx Context,
|
||||
ty: BasicTypeEnum<'ctx>,
|
||||
) -> Result<(), super::ModelError> {
|
||||
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_impl(tyctx, ctx, elem_ty)
|
||||
.map_err(|err| err.under_context("a PointerType"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Element: Model> Model for PtrModel<Element> {
|
||||
type Value<'ctx> = PointerValue<'ctx>;
|
||||
type Type<'ctx> = PointerType<'ctx>;
|
||||
|
||||
fn get_type<'ctx>(&self, tyctx: TypeContext<'ctx>, ctx: &'ctx Context) -> Self::Type<'ctx> {
|
||||
self.0.get_type(tyctx, ctx).ptr_type(AddressSpace::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Element: Model> PtrModel<Element> {
|
||||
/// Return a ***constant*** nullptr.
|
||||
pub fn nullptr<'ctx>(
|
||||
&self,
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &'ctx Context,
|
||||
) -> Ptr<'ctx, Element> {
|
||||
let ptr = self.get_type(tyctx, ctx).const_null();
|
||||
self.believe_value(ptr)
|
||||
}
|
||||
|
||||
pub fn transmute<'ctx>(
|
||||
&self,
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
ptr: PointerValue<'ctx>,
|
||||
name: &str,
|
||||
) -> Ptr<'ctx, Element> {
|
||||
let ptr = ctx.builder.build_pointer_cast(ptr, self.get_type(tyctx, ctx.ctx), name).unwrap();
|
||||
self.believe_value(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx, Element: Model> Ptr<'ctx, Element> {
|
||||
/// Offset the pointer by [`inkwell::builder::Builder::build_in_bounds_gep`].
|
||||
pub fn offset(
|
||||
&self,
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
offset: IntValue<'ctx>,
|
||||
name: &str,
|
||||
) -> Ptr<'ctx, Element> {
|
||||
let new_ptr =
|
||||
unsafe { ctx.builder.build_in_bounds_gep(self.value, &[offset], name).unwrap() };
|
||||
self.model.check_value(tyctx, ctx.ctx, new_ptr).unwrap()
|
||||
}
|
||||
|
||||
// Load the `i`-th element (0-based) on the array with [`inkwell::builder::Builder::build_in_bounds_gep`].
|
||||
pub fn ix(
|
||||
&self,
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
i: IntValue<'ctx>,
|
||||
name: &str,
|
||||
) -> Instance<'ctx, Element> {
|
||||
self.offset(tyctx, ctx, i, name).load(tyctx, ctx, name)
|
||||
}
|
||||
|
||||
/// Load the value with [`inkwell::builder::Builder::build_load`].
|
||||
pub fn load(
|
||||
&self,
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
name: &str,
|
||||
) -> Instance<'ctx, Element> {
|
||||
let value = ctx.builder.build_load(self.value, name).unwrap();
|
||||
self.model.0.check_value(tyctx, 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, Element>) {
|
||||
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 transmute<NewElement: Model>(
|
||||
&self,
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
new_model: NewElement,
|
||||
name: &str,
|
||||
) -> Ptr<'ctx, NewElement> {
|
||||
PtrModel(new_model).transmute(tyctx, ctx, self.value, name)
|
||||
}
|
||||
|
||||
/// Check if the pointer is null with [`inkwell::builder::Builder::build_is_null`].
|
||||
pub fn is_null(&self, ctx: &CodeGenContext<'ctx, '_>, name: &str) -> Int<'ctx, Bool> {
|
||||
let bool_model = IntModel(Bool);
|
||||
let value = ctx.builder.build_is_null(self.value, name).unwrap();
|
||||
bool_model.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, '_>, name: &str) -> Int<'ctx, Bool> {
|
||||
let bool_model = IntModel(Bool);
|
||||
let value = ctx.builder.build_is_not_null(self.value, name).unwrap();
|
||||
bool_model.believe_value(value)
|
||||
}
|
||||
}
|
72
nac3core/src/codegen/model/slice.rs
Normal file
72
nac3core/src/codegen/model/slice.rs
Normal file
@ -0,0 +1,72 @@
|
||||
use crate::codegen::{CodeGenContext, CodeGenerator};
|
||||
|
||||
use super::*;
|
||||
|
||||
/// A slice - literally just a pointer and a length value.
|
||||
///
|
||||
/// NOTE: This is NOT a [`Model`].
|
||||
pub struct ArraySlice<'ctx, Len: IntKind, Item: Model> {
|
||||
pub base: Ptr<'ctx, Item>,
|
||||
pub len: Int<'ctx, Len>,
|
||||
}
|
||||
|
||||
impl<'ctx, Len: IntKind, Item: Model> ArraySlice<'ctx, Len, Item> {
|
||||
/// Get the `idx`-nth element of this [`ArraySlice`], but doesn't do an assertion to see if `idx` is out of bounds or not.
|
||||
///
|
||||
/// Also see [`ArraySlice::ix`].
|
||||
pub fn ix_unchecked(
|
||||
&self,
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
idx: Int<'ctx, Len>,
|
||||
name: &str,
|
||||
) -> Ptr<'ctx, Item> {
|
||||
let element_ptr = unsafe {
|
||||
ctx.builder.build_in_bounds_gep(self.base.value, &[idx.value], name).unwrap()
|
||||
};
|
||||
self.base.model.check_value(tyctx, ctx.ctx, element_ptr).unwrap()
|
||||
}
|
||||
|
||||
/// Call [`ArraySlice::ix_unchecked`], but checks if `idx` is in bounds, otherwise a runtime `IndexError` will be thrown.
|
||||
pub fn ix<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
idx: Int<'ctx, Len>,
|
||||
name: &str,
|
||||
) -> Ptr<'ctx, Item> {
|
||||
let tyctx = generator.type_context(ctx.ctx);
|
||||
let len_model = IntModel(Len::default());
|
||||
|
||||
// Assert `0 <= idx < length` and throw an Exception if `idx` is out of bounds
|
||||
let lower_bounded = ctx
|
||||
.builder
|
||||
.build_int_compare(
|
||||
inkwell::IntPredicate::SLE,
|
||||
len_model.constant(tyctx, ctx.ctx, 0).value,
|
||||
idx.value,
|
||||
"lower_bounded",
|
||||
)
|
||||
.unwrap();
|
||||
let upper_bounded = ctx
|
||||
.builder
|
||||
.build_int_compare(
|
||||
inkwell::IntPredicate::SLT,
|
||||
idx.value,
|
||||
self.len.value,
|
||||
"upper_bounded",
|
||||
)
|
||||
.unwrap();
|
||||
let bounded = ctx.builder.build_and(lower_bounded, upper_bounded, "bounded").unwrap();
|
||||
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(idx.value), Some(self.len.value), None],
|
||||
ctx.current_loc
|
||||
);
|
||||
|
||||
self.ix_unchecked(tyctx, ctx, idx, name)
|
||||
}
|
||||
}
|
174
nac3core/src/codegen/model/structure.rs
Normal file
174
nac3core/src/codegen/model/structure.rs
Normal file
@ -0,0 +1,174 @@
|
||||
use std::fmt;
|
||||
|
||||
use inkwell::{
|
||||
context::Context,
|
||||
types::{BasicType, BasicTypeEnum, StructType},
|
||||
values::StructValue,
|
||||
};
|
||||
use itertools::izip;
|
||||
|
||||
use crate::codegen::CodeGenContext;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct GepField<M: Model> {
|
||||
pub gep_index: u64,
|
||||
pub name: &'static str,
|
||||
pub model: M,
|
||||
}
|
||||
|
||||
pub trait FieldVisitor {
|
||||
type Field<M: Model + 'static>;
|
||||
|
||||
fn add<M: Model + 'static>(&mut self, name: &'static str) -> Self::Field<M>;
|
||||
}
|
||||
|
||||
pub struct GepFieldVisitor {
|
||||
gep_index_counter: u64,
|
||||
}
|
||||
|
||||
impl FieldVisitor for GepFieldVisitor {
|
||||
type Field<M: Model + 'static> = GepField<M>;
|
||||
|
||||
fn add<M: Model + 'static>(&mut self, name: &'static str) -> Self::Field<M> {
|
||||
let gep_index = self.gep_index_counter;
|
||||
self.gep_index_counter += 1;
|
||||
Self::Field { gep_index, name, model: M::default() }
|
||||
}
|
||||
}
|
||||
|
||||
struct TypeFieldVisitor<'ctx> {
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &'ctx Context,
|
||||
field_types: Vec<BasicTypeEnum<'ctx>>,
|
||||
}
|
||||
|
||||
impl<'ctx> FieldVisitor for TypeFieldVisitor<'ctx> {
|
||||
type Field<M: Model + 'static> = ();
|
||||
|
||||
fn add<M: Model + 'static>(&mut self, _name: &'static str) -> Self::Field<M> {
|
||||
self.field_types.push(M::default().get_type(self.tyctx, self.ctx).as_basic_type_enum());
|
||||
}
|
||||
}
|
||||
|
||||
struct CheckTypeEntry {
|
||||
check_type: Box<dyn CheckType + 'static>,
|
||||
name: &'static str,
|
||||
}
|
||||
|
||||
struct CheckTypeFieldVisitor<'ctx> {
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &'ctx Context,
|
||||
check_types: Vec<CheckTypeEntry>,
|
||||
}
|
||||
|
||||
impl<'ctx> FieldVisitor for CheckTypeFieldVisitor<'ctx> {
|
||||
type Field<M: Model + 'static> = ();
|
||||
|
||||
fn add<M: Model + 'static>(&mut self, name: &'static str) -> Self::Field<M> {
|
||||
self.check_types.push(CheckTypeEntry { check_type: Box::<M>::default(), name });
|
||||
}
|
||||
}
|
||||
|
||||
pub trait StructKind: fmt::Debug + Clone + Copy + Default {
|
||||
type Fields<F: FieldVisitor>;
|
||||
|
||||
fn visit_fields<F: FieldVisitor>(&self, visitor: &mut F) -> Self::Fields<F>;
|
||||
|
||||
fn fields(&self) -> Self::Fields<GepFieldVisitor> {
|
||||
self.visit_fields(&mut GepFieldVisitor { gep_index_counter: 0 })
|
||||
}
|
||||
|
||||
fn get_struct_type<'ctx>(
|
||||
&self,
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &'ctx Context,
|
||||
) -> StructType<'ctx> {
|
||||
let mut visitor = TypeFieldVisitor { tyctx, ctx, field_types: Vec::new() };
|
||||
self.visit_fields(&mut visitor);
|
||||
|
||||
ctx.struct_type(&visitor.field_types, false)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct StructModel<S: StructKind>(pub S);
|
||||
pub type Struct<'ctx, S> = Instance<'ctx, StructModel<S>>;
|
||||
|
||||
impl<S: StructKind> CheckType for StructModel<S> {
|
||||
fn check_type_impl<'ctx>(
|
||||
&self,
|
||||
tyctx: TypeContext<'ctx>,
|
||||
ctx: &'ctx Context,
|
||||
ty: BasicTypeEnum<'ctx>,
|
||||
) -> Result<(), ModelError> {
|
||||
let ty = ty.as_basic_type_enum();
|
||||
let Ok(ty) = StructType::try_from(ty) else {
|
||||
return Err(ModelError(format!("Expecting StructType, but got {ty:?}")));
|
||||
};
|
||||
|
||||
let field_types = ty.get_field_types();
|
||||
|
||||
let check_types = {
|
||||
let mut builder = CheckTypeFieldVisitor { tyctx, ctx, check_types: Vec::new() };
|
||||
self.0.visit_fields(&mut builder);
|
||||
builder.check_types
|
||||
};
|
||||
|
||||
if check_types.len() != field_types.len() {
|
||||
return Err(ModelError(format!(
|
||||
"Expecting StructType to have {} field(s), but got {} field(s)",
|
||||
check_types.len(),
|
||||
field_types.len()
|
||||
)));
|
||||
}
|
||||
|
||||
for (field_i, (entry, field_type)) in izip!(check_types, field_types).enumerate() {
|
||||
let field_at = field_i + 1;
|
||||
|
||||
entry.check_type.check_type_impl(tyctx, ctx, field_type).map_err(|err| {
|
||||
err.under_context(format!("struct field #{field_at} '{}'", entry.name).as_str())
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: StructKind> Model for StructModel<S> {
|
||||
type Value<'ctx> = StructValue<'ctx>;
|
||||
type Type<'ctx> = StructType<'ctx>;
|
||||
|
||||
fn get_type<'ctx>(&self, tyctx: TypeContext<'ctx>, ctx: &'ctx Context) -> Self::Type<'ctx> {
|
||||
self.0.get_struct_type(tyctx, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx, S: StructKind> Ptr<'ctx, StructModel<S>> {
|
||||
pub fn gep<M, GetField>(
|
||||
&self,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
get_field: GetField,
|
||||
) -> Ptr<'ctx, M>
|
||||
where
|
||||
M: Model,
|
||||
GetField: FnOnce(S::Fields<GepFieldVisitor>) -> GepField<M>,
|
||||
{
|
||||
let field = get_field(self.model.0 .0.fields());
|
||||
let llvm_i32 = ctx.ctx.i32_type(); // must be i32, if its i64 then rust segfaults
|
||||
|
||||
let ptr = unsafe {
|
||||
ctx.builder
|
||||
.build_in_bounds_gep(
|
||||
self.value,
|
||||
&[llvm_i32.const_zero(), llvm_i32.const_int(field.gep_index, false)],
|
||||
field.name,
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let ptr_model = PtrModel(field.model);
|
||||
ptr_model.believe_value(ptr)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user