Initial commit
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
use crate::{
|
||||
functions::Function,
|
||||
types::{
|
||||
Type,
|
||||
derivations::{FuncT, TypeDerivations},
|
||||
intrinsics::*,
|
||||
},
|
||||
values::{Const, Int},
|
||||
};
|
||||
use leaf_allocators::SyncAllocator;
|
||||
use scc::HashMap;
|
||||
use std::{hash::Hash, sync::OnceLock};
|
||||
|
||||
pub type Ctx<'l> = &'l Context<'l>;
|
||||
|
||||
pub struct Context<'l> {
|
||||
pub(crate) alloc: &'l dyn SyncAllocator,
|
||||
pub(crate) derivations: TypeDerivations<'l>,
|
||||
|
||||
constants: HashMap<&'l Const<'l>, &'l Const<'l>>,
|
||||
intrinsics: OnceLock<Box<intrinsic_types::IntrinsicTypesArray<'l>>>,
|
||||
}
|
||||
|
||||
impl Eq for Context<'_> {}
|
||||
|
||||
impl PartialEq for Context<'_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
std::ptr::eq(self, other)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Context<'_> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
(self as *const Self).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'l> Context<'l> {
|
||||
pub fn new(alloc: &'l dyn SyncAllocator) -> &'l Context<'l> {
|
||||
let ctx = alloc.alloc(Self {
|
||||
alloc,
|
||||
constants: HashMap::new(),
|
||||
derivations: TypeDerivations::new(alloc),
|
||||
intrinsics: OnceLock::new(),
|
||||
});
|
||||
ctx.intrinsics
|
||||
.set(Box::new(intrinsic_types::make_intrinsic_types(ctx)))
|
||||
.unwrap();
|
||||
ctx
|
||||
}
|
||||
|
||||
pub fn create_function(&'l self, ty: &'l FuncT<'l>) -> &'l Function<'l> {
|
||||
self.alloc.alloc(Function {
|
||||
ty,
|
||||
body: OnceLock::new(),
|
||||
ctx: self,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CreateConst<'l, T> {
|
||||
fn create_const(&'l self, value: T) -> &'l Const<'l>;
|
||||
}
|
||||
|
||||
macro_rules! create_const {
|
||||
($(impl $ty:ty : $expr:expr,)*) => {
|
||||
$(
|
||||
impl<'l> CreateConst<'l, $ty> for Context<'l> {
|
||||
fn create_const(&'l self, value: $ty) -> &'l Const<'l> {
|
||||
let constant = ($expr)(self, value);
|
||||
if let Some(existing) = self.constants.get_sync(&constant) {
|
||||
return *existing;
|
||||
}
|
||||
let constant = self.alloc.alloc(constant);
|
||||
*self.constants.entry_sync(constant).or_insert(constant)
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
create_const! {
|
||||
impl i8: |ctx, val| Const::Int(Int::I8(val), ctx),
|
||||
impl i16: |ctx, val| Const::Int(Int::I16(val), ctx),
|
||||
impl i32: |ctx, val| Const::Int(Int::I32(val), ctx),
|
||||
impl i64: |ctx, val| Const::Int(Int::I64(val), ctx),
|
||||
impl i128: |ctx, val| Const::Int(Int::I128(val), ctx),
|
||||
|
||||
impl u8: |ctx, val| Const::Int(Int::U8(val), ctx),
|
||||
impl u16: |ctx, val| Const::Int(Int::U16(val), ctx),
|
||||
impl u32: |ctx, val| Const::Int(Int::U32(val), ctx),
|
||||
impl u64: |ctx, val| Const::Int(Int::U64(val), ctx),
|
||||
impl u128: |ctx, val| Const::Int(Int::U128(val), ctx),
|
||||
|
||||
impl (): |ctx, _| Const::Void(ctx),
|
||||
impl char: |ctx, val| Const::Char(val, ctx),
|
||||
impl bool: |ctx, val| Const::Bool(val, ctx),
|
||||
impl &'l str: |ctx, val| Const::Str(val, ctx),
|
||||
}
|
||||
|
||||
mod intrinsic_types {
|
||||
use super::*;
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
macro_rules! create_intrinsics {
|
||||
($(($id:ident, $fn_id:ident, $init:expr),)*) => {
|
||||
pub enum IntrinsicTypeKind {
|
||||
$($id,)*
|
||||
COUNT,
|
||||
}
|
||||
|
||||
pub type IntrinsicTypesArray<'l> = [Type<'l>; IntrinsicTypeKind::COUNT as usize];
|
||||
|
||||
pub fn make_intrinsic_types<'l>(ctx: Ctx<'l>) -> IntrinsicTypesArray<'l> {
|
||||
unsafe {
|
||||
const C: usize = IntrinsicTypeKind::COUNT as usize;
|
||||
let mut intrinsics = [MaybeUninit::<Type>::uninit(); C];
|
||||
|
||||
$(
|
||||
intrinsics[IntrinsicTypeKind::$id as usize]
|
||||
.write((&*ctx.alloc.alloc($init(ctx))).into());
|
||||
)*
|
||||
|
||||
std::mem::transmute(intrinsics)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'l> Context<'l> {
|
||||
$(
|
||||
#[inline]
|
||||
pub fn $fn_id(&'l self) -> Type<'l> {
|
||||
self.get_intrinsic(IntrinsicTypeKind::$id)
|
||||
}
|
||||
)*
|
||||
|
||||
#[inline]
|
||||
fn get_intrinsic(&'l self, intrinsic: IntrinsicTypeKind) -> Type<'l> {
|
||||
unsafe {
|
||||
self.intrinsics
|
||||
.get()
|
||||
.unwrap_unchecked()
|
||||
.get_unchecked(intrinsic as usize)
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
create_intrinsics! {
|
||||
(Void, void_t, |ctx| VoidT { ctx }),
|
||||
(Char, char_t, |ctx| CharT { ctx }),
|
||||
(Bool, bool_t, |ctx| BoolT { ctx }),
|
||||
(Type, type_t, |ctx| TypeT { ctx, }),
|
||||
(ConstStr, const_str_t, |ctx| ConstStrT { ctx, }),
|
||||
|
||||
(I8, i8_t, |ctx| IntT { ctx, signed: true, precision: 8 }),
|
||||
(I16, i16_t, |ctx| IntT { ctx, signed: true, precision: 16 }),
|
||||
(I32, i32_t, |ctx| IntT { ctx, signed: true, precision: 32 }),
|
||||
(I64, i64_t, |ctx| IntT { ctx, signed: true, precision: 64 }),
|
||||
(I128, i128_t, |ctx| IntT { ctx, signed: true, precision: 128 }),
|
||||
(ISize, isize_t, |ctx| IntT { ctx, signed: true, precision: u32::MAX }),
|
||||
|
||||
(U8, u8_t, |ctx| IntT { ctx, signed: false, precision: 8 }),
|
||||
(U16, u16_t, |ctx| IntT { ctx, signed: false, precision: 16 }),
|
||||
(U32, u32_t, |ctx| IntT { ctx, signed: false, precision: 32 }),
|
||||
(U64, u64_t, |ctx| IntT { ctx, signed: false, precision: 64 }),
|
||||
(U128, u128_t, |ctx| IntT { ctx, signed: false, precision: 128 }),
|
||||
(USize, usize_t, |ctx| IntT { ctx, signed: false, precision: u32::MAX }),
|
||||
|
||||
(F16, f16_t, |ctx| FloatT { ctx, precision: 16 }),
|
||||
(F32, f32_t, |ctx| FloatT { ctx, precision: 32 }),
|
||||
(F64, f64_t, |ctx| FloatT { ctx, precision: 64 }),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
use crate::{
|
||||
context::Ctx,
|
||||
functions::{Function, FunctionBody},
|
||||
types::{Type, derivations::*},
|
||||
values::{Value, ValueFlags},
|
||||
};
|
||||
use derive_more::Debug;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
hash::Hash,
|
||||
ops::Deref,
|
||||
sync::{
|
||||
OnceLock,
|
||||
atomic::{AtomicU32, Ordering},
|
||||
},
|
||||
u32,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[debug("%{}: {variant:?}", self.id())]
|
||||
pub struct Instruction<'l> {
|
||||
id: AtomicU32,
|
||||
pub parent_block: &'l Block<'l>,
|
||||
pub variant: InstructionVariant<'l>,
|
||||
}
|
||||
|
||||
impl Eq for Instruction<'_> {}
|
||||
|
||||
impl PartialEq for Instruction<'_> {
|
||||
#[inline]
|
||||
fn eq(&self, _: &Self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Instruction<'_> {
|
||||
#[inline]
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
(self as *const Self).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'l> Deref for Instruction<'l> {
|
||||
type Target = InstructionVariant<'l>;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.variant
|
||||
}
|
||||
}
|
||||
|
||||
impl<'l> Into<Value<'l>> for &'l Instruction<'l> {
|
||||
#[inline]
|
||||
fn into(self) -> Value<'l> {
|
||||
Value::Instruction(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'l> Instruction<'l> {
|
||||
#[inline]
|
||||
pub fn ctx(&self) -> Ctx<'l> {
|
||||
self.parent_block.func.ctx
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> u32 {
|
||||
self.id.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn value_flags(&self) -> ValueFlags {
|
||||
match self.variant {
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value_ty(&self) -> Type<'l> {
|
||||
match self.variant {
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub enum InstructionVariant<'l> {
|
||||
#[debug("stackalloc {_0}")]
|
||||
StackAlloc(Type<'l>),
|
||||
|
||||
#[debug("gcalloc {_0}")]
|
||||
GCAlloc(Type<'l>),
|
||||
|
||||
#[debug("load {_0}")]
|
||||
Load(Value<'l>),
|
||||
|
||||
#[debug("store {_0}, {_1}")]
|
||||
Store(Value<'l>, Value<'l>),
|
||||
|
||||
#[debug("return{}", _0.map(|v| format!(" {v}")).unwrap_or_default())]
|
||||
Return(Option<Value<'l>>),
|
||||
}
|
||||
|
||||
impl InstructionVariant<'_> {
|
||||
pub fn is_block_termination(&self) -> bool {
|
||||
match self {
|
||||
Self::Return(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Block<'l> {
|
||||
pub id: u32,
|
||||
func: &'l Function<'l>,
|
||||
instructions: OnceLock<Vec<&'l Instruction<'l>>>,
|
||||
}
|
||||
|
||||
impl<'l> Block<'l> {
|
||||
#[inline]
|
||||
pub fn instructions(&self) -> &[&'l Instruction<'l>] {
|
||||
match self.instructions.get() {
|
||||
None => &[],
|
||||
Some(v) => v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BlockBuilder<'l> {
|
||||
block: &'l Block<'l>,
|
||||
instructions: Vec<&'l Instruction<'l>>,
|
||||
}
|
||||
|
||||
pub type BlockBuilderError<'l> = Cow<'l, str>;
|
||||
pub type BlockBuilderResult<'l, T> = Result<T, BlockBuilderError<'l>>;
|
||||
|
||||
impl<'l> BlockBuilder<'l> {
|
||||
pub fn stack_alloc(&mut self, ty: Type<'l>) -> BlockBuilderResult<'l, Value<'l>> {
|
||||
let inst = self.push_instruction(InstructionVariant::StackAlloc(ty))?;
|
||||
Ok(inst.into())
|
||||
}
|
||||
|
||||
pub fn store(
|
||||
&mut self,
|
||||
target: Value<'l>,
|
||||
value: Value<'l>,
|
||||
) -> BlockBuilderResult<'l, Value<'l>> {
|
||||
let value_ty = value.ty();
|
||||
let target_ty = target.ty();
|
||||
match target_ty {
|
||||
Type::Ptr(PtrT {
|
||||
base,
|
||||
mutable: true,
|
||||
}) if *base == value_ty => {}
|
||||
|
||||
Type::Ref(RefT {
|
||||
base,
|
||||
mutable: true,
|
||||
}) if *base == value_ty => {}
|
||||
|
||||
_ => {
|
||||
return Err(format!(
|
||||
"Cannot store value of type `{value_ty}` into target of type `{target_ty}.`"
|
||||
)
|
||||
.into());
|
||||
}
|
||||
};
|
||||
let inst = self.push_instruction(InstructionVariant::Store(target, value))?;
|
||||
Ok(inst.into())
|
||||
}
|
||||
|
||||
pub fn load(&mut self, value: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
|
||||
let value_ty = value.ty();
|
||||
match value_ty {
|
||||
Type::Ptr(PtrT { .. }) => {}
|
||||
Type::Ref(RefT { .. }) => {}
|
||||
_ => {
|
||||
return Err(format!("Cannot load value of type `{}`.`", value_ty).into());
|
||||
}
|
||||
}
|
||||
let inst = self.push_instruction(InstructionVariant::Load(value))?;
|
||||
Ok(inst.into())
|
||||
}
|
||||
|
||||
pub fn ret(&mut self, value: Option<Value<'l>>) -> BlockBuilderResult<'l, Value<'l>> {
|
||||
let ret_t = self.block.func.ty.ret_t;
|
||||
let value_ty = match value {
|
||||
Some(v) => v.ty(),
|
||||
None => self.block.func.ctx.void_t(),
|
||||
};
|
||||
if value_ty != ret_t {
|
||||
return Err(format!(
|
||||
"Cannot return value of type `{value_ty}`. Expected type `{ret_t}`."
|
||||
)
|
||||
.into());
|
||||
}
|
||||
let inst = self.push_instruction(InstructionVariant::Return(value))?;
|
||||
Ok(inst.into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_termination(&self) -> bool {
|
||||
match self.instructions.as_slice() {
|
||||
[.., i] => i.is_block_termination(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(self) -> BlockBuilderResult<'l, &'l Block<'l>> {
|
||||
if !self.has_termination() {
|
||||
return Err(format!("Block #{} has not termination.", self.block.id).into());
|
||||
}
|
||||
self.block.instructions.set(self.instructions).unwrap();
|
||||
Ok(self.block)
|
||||
}
|
||||
|
||||
fn push_instruction(
|
||||
&mut self,
|
||||
variant: InstructionVariant<'l>,
|
||||
) -> BlockBuilderResult<'l, &'l Instruction<'l>> {
|
||||
if self.has_termination() {
|
||||
return Err(format!("Block #{} has already terminated", self.block.id).into());
|
||||
}
|
||||
let instruction = &*self.block.func.ctx.alloc.alloc(Instruction {
|
||||
id: AtomicU32::new(u32::MAX),
|
||||
parent_block: self.block,
|
||||
variant,
|
||||
});
|
||||
self.instructions.push(instruction);
|
||||
Ok(instruction)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FunctionBodyBuilder<'l> {
|
||||
current_block: usize,
|
||||
func: &'l Function<'l>,
|
||||
blocks: Vec<BlockBuilder<'l>>,
|
||||
}
|
||||
|
||||
impl<'l> FunctionBodyBuilder<'l> {
|
||||
pub fn new(func: &'l Function<'l>) -> Self {
|
||||
let mut builder = Self {
|
||||
func,
|
||||
blocks: vec![],
|
||||
current_block: 0,
|
||||
};
|
||||
builder.create_block();
|
||||
builder
|
||||
}
|
||||
|
||||
pub fn current_block(&self) -> &'l Block<'l> {
|
||||
self.blocks[self.current_block].block
|
||||
}
|
||||
|
||||
pub fn set_current_block(&mut self, block: &'l Block<'l>) -> Option<&'l Block<'l>> {
|
||||
match std::ptr::eq(block.func, self.func) {
|
||||
false => None,
|
||||
true => {
|
||||
let current = self.current_block();
|
||||
self.current_block = block.id as usize;
|
||||
Some(current)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_block(&mut self) -> &'l Block<'l> {
|
||||
let block = &*self.func.ctx.alloc.alloc(Block {
|
||||
id: 0,
|
||||
func: self.func,
|
||||
instructions: OnceLock::new(),
|
||||
});
|
||||
let builder = BlockBuilder {
|
||||
block,
|
||||
instructions: vec![],
|
||||
};
|
||||
self.blocks.push(builder);
|
||||
block
|
||||
}
|
||||
|
||||
pub fn build(self) -> Result<&'l FunctionBody<'l>, Cow<'l, str>> {
|
||||
let mut next_id = 0..;
|
||||
let mut blocks = Vec::with_capacity(self.blocks.len());
|
||||
for block in self.blocks {
|
||||
let block = block.build()?;
|
||||
for inst in block.instructions() {
|
||||
inst.id.store(next_id.next().unwrap(), Ordering::Relaxed);
|
||||
}
|
||||
blocks.push(block);
|
||||
}
|
||||
if self.func.body.set(FunctionBody { blocks }).is_err() {
|
||||
return Err("Function body already exists.".into());
|
||||
}
|
||||
Ok(self.func.body.get().unwrap())
|
||||
}
|
||||
|
||||
pub fn stack_alloc(&mut self, ty: Type<'l>) -> BlockBuilderResult<'l, Value<'l>> {
|
||||
self.current_builder().stack_alloc(ty)
|
||||
}
|
||||
|
||||
pub fn store(
|
||||
&mut self,
|
||||
target: Value<'l>,
|
||||
value: Value<'l>,
|
||||
) -> BlockBuilderResult<'l, Value<'l>> {
|
||||
self.current_builder().store(target, value)
|
||||
}
|
||||
|
||||
pub fn load(&mut self, value: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
|
||||
self.current_builder().load(value)
|
||||
}
|
||||
|
||||
pub fn ret(&mut self, value: Option<Value<'l>>) -> BlockBuilderResult<'l, Value<'l>> {
|
||||
self.current_builder().ret(value)
|
||||
}
|
||||
|
||||
fn current_builder(&mut self) -> &mut BlockBuilder<'l> {
|
||||
&mut self.blocks[self.current_block]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
use crate::{
|
||||
context::Ctx,
|
||||
functions::ir::{Block, FunctionBodyBuilder},
|
||||
types::derivations::FuncT,
|
||||
};
|
||||
use std::{fmt::Debug as FmtDebug, sync::OnceLock};
|
||||
|
||||
pub mod ir;
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct Function<'l> {
|
||||
pub ty: &'l FuncT<'l>,
|
||||
pub(crate) body: OnceLock<FunctionBody<'l>>,
|
||||
|
||||
pub(crate) ctx: Ctx<'l>,
|
||||
}
|
||||
|
||||
impl<'l> Function<'l> {
|
||||
pub fn body(&self) -> Option<&FunctionBody<'l>> {
|
||||
self.body.get()
|
||||
}
|
||||
|
||||
pub fn create_body(&'l self) -> Option<FunctionBodyBuilder<'l>> {
|
||||
match self.body() {
|
||||
Some(_) => None,
|
||||
None => Some(FunctionBodyBuilder::new(self)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FmtDebug for Function<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let body: &dyn FmtDebug = match self.body() {
|
||||
Some(b) => b,
|
||||
None => &None::<()>,
|
||||
};
|
||||
|
||||
f.debug_struct("Function")
|
||||
.field("ty", &format_args!("{}", self.ty))
|
||||
.field("body", body)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct FunctionBody<'l> {
|
||||
pub blocks: Vec<&'l Block<'l>>,
|
||||
}
|
||||
|
||||
impl<'l> FmtDebug for FunctionBody<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
struct BlockDbg<'l>(&'l Block<'l>);
|
||||
|
||||
impl FmtDebug for BlockDbg<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use std::fmt::Write;
|
||||
let mut name = format!("#{}:", self.0.id);
|
||||
let mut dbg = f.debug_struct(&name);
|
||||
for inst in self.0.instructions() {
|
||||
name.clear();
|
||||
write!(name, "%{}", inst.id())?;
|
||||
dbg.field(&name, &format_args!("{:?}", inst.variant));
|
||||
}
|
||||
dbg.finish()
|
||||
}
|
||||
}
|
||||
|
||||
f.debug_set()
|
||||
.entries(self.blocks.iter().map(|b| BlockDbg(b)))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
pub mod context;
|
||||
pub mod functions;
|
||||
pub mod types;
|
||||
pub mod values;
|
||||
@@ -0,0 +1,15 @@
|
||||
use leaf_allocators::SyncArenaAllocator;
|
||||
use leaf_assembly::context::{Context, CreateConst};
|
||||
use leaf_assembly::types::derivations::MakeTypeDerivations;
|
||||
|
||||
fn main() {
|
||||
let allocator = SyncArenaAllocator::default();
|
||||
let context = Context::new(&allocator);
|
||||
let func = context.create_function(context.u32_t().make_fn([]));
|
||||
let mut builder = func.create_body().unwrap();
|
||||
builder
|
||||
.ret(Some(context.create_const(42u32).into()))
|
||||
.unwrap();
|
||||
builder.build().unwrap();
|
||||
println!("{func:#?}");
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
use crate::types::*;
|
||||
use derive_more::{Debug, Display};
|
||||
use leaf_allocators::SyncAllocator;
|
||||
use scc::HashMap;
|
||||
use std::{fmt::Display, hash::Hash, sync::Arc};
|
||||
|
||||
pub struct TypeDerivations<'l> {
|
||||
alloc: &'l dyn SyncAllocator,
|
||||
ptr_t: HashMap<(Type<'l>, bool), Type<'l>>,
|
||||
ref_t: HashMap<(Type<'l>, bool), Type<'l>>,
|
||||
fun_t: HashMap<(Type<'l>, Arc<[Type<'l>]>), Type<'l>>,
|
||||
}
|
||||
|
||||
impl<'l> TypeDerivations<'l> {
|
||||
pub fn new(alloc: &'l dyn SyncAllocator) -> Self {
|
||||
Self {
|
||||
alloc,
|
||||
ptr_t: HashMap::new(),
|
||||
ref_t: HashMap::new(),
|
||||
fun_t: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_ptr(&self, base: Type<'l>, mutable: bool) -> &'l PtrT<'l> {
|
||||
let Type::Ptr(ty) = *self
|
||||
.ptr_t
|
||||
.entry_sync((base, mutable))
|
||||
.or_insert_with(|| (&*self.alloc.alloc(PtrT { base, mutable })).into())
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
ty
|
||||
}
|
||||
|
||||
pub fn make_ref(&'l self, base: Type<'l>, mutable: bool) -> &'l RefT<'l> {
|
||||
let Type::Ref(ty) = *self
|
||||
.ref_t
|
||||
.entry_sync((base, mutable))
|
||||
.or_insert_with(|| (&*self.alloc.alloc(RefT { base, mutable })).into())
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
ty
|
||||
}
|
||||
|
||||
pub fn make_fn(&'l self, ret_t: Type<'l>, par_t: Arc<[Type<'l>]>) -> &'l FuncT<'l> {
|
||||
let Type::Func(ty) = *self
|
||||
.fun_t
|
||||
.entry_sync((ret_t, par_t.clone()))
|
||||
.or_insert_with(|| {
|
||||
let ctx = ret_t.ctx();
|
||||
for ty in par_t.iter() {
|
||||
if ty.ctx() != ctx {
|
||||
panic!("All types must share the same context.");
|
||||
}
|
||||
}
|
||||
(&*self.alloc.alloc(FuncT { ret_t, par_t })).into()
|
||||
})
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
ty
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for TypeDerivations<'_> {}
|
||||
|
||||
impl PartialEq for TypeDerivations<'_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
std::ptr::eq(self, other)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for TypeDerivations<'_> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
(self as *const Self).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Display, PartialEq, Eq, Hash)]
|
||||
#[display("*{}{}", if *mutable { "mut " } else { "" }, *base)]
|
||||
pub struct PtrT<'l> {
|
||||
#[debug("{base}")]
|
||||
pub base: Type<'l>,
|
||||
pub mutable: bool,
|
||||
}
|
||||
|
||||
impl<'l> Into<Type<'l>> for &'l PtrT<'l> {
|
||||
fn into(self) -> Type<'l> {
|
||||
Type::Ptr(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Display, PartialEq, Eq, Hash)]
|
||||
#[display("&{}{}", if *mutable { "mut " } else { "" }, *base)]
|
||||
pub struct RefT<'l> {
|
||||
#[debug("{base}")]
|
||||
pub base: Type<'l>,
|
||||
pub mutable: bool,
|
||||
}
|
||||
|
||||
impl<'l> Into<Type<'l>> for &'l RefT<'l> {
|
||||
fn into(self) -> Type<'l> {
|
||||
Type::Ref(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct FuncT<'l> {
|
||||
pub ret_t: Type<'l>,
|
||||
pub par_t: Arc<[Type<'l>]>,
|
||||
}
|
||||
|
||||
impl Display for FuncT<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut separator = "";
|
||||
write!(f, "fn(")?;
|
||||
for ty in &*self.par_t {
|
||||
write!(f, "{separator}{ty}")?;
|
||||
separator = ", ";
|
||||
}
|
||||
write!(f, ") -> {}", self.ret_t)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'l> Into<Type<'l>> for &'l FuncT<'l> {
|
||||
fn into(self) -> Type<'l> {
|
||||
Type::Func(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MakeTypeDerivations<'l> {
|
||||
fn make_ptr(self, mutable: bool) -> &'l PtrT<'l>;
|
||||
fn make_ref(self, mutable: bool) -> &'l RefT<'l>;
|
||||
fn make_fn(self, par_t: impl IntoIterator<Item = Type<'l>>) -> &'l FuncT<'l>;
|
||||
}
|
||||
|
||||
impl<'l, T: 'l + Into<Type<'l>>> MakeTypeDerivations<'l> for T {
|
||||
fn make_ptr(self, mutable: bool) -> &'l PtrT<'l> {
|
||||
let ty = self.into();
|
||||
ty.ctx().derivations.make_ptr(ty, mutable)
|
||||
}
|
||||
|
||||
fn make_ref(self, mutable: bool) -> &'l RefT<'l> {
|
||||
let ty = self.into();
|
||||
ty.ctx().derivations.make_ref(ty, mutable)
|
||||
}
|
||||
|
||||
fn make_fn(self, par_t: impl IntoIterator<Item = Type<'l>>) -> &'l FuncT<'l> {
|
||||
let ty = self.into();
|
||||
ty.ctx()
|
||||
.derivations
|
||||
.make_fn(ty, par_t.into_iter().collect())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
use crate::{context::Ctx, types::Type};
|
||||
use derive_more::{Debug, Display};
|
||||
|
||||
#[derive(Debug, Display, PartialEq, Eq, Hash)]
|
||||
#[display("type")]
|
||||
#[debug("type")]
|
||||
pub struct TypeT<'l> {
|
||||
pub(crate) ctx: Ctx<'l>,
|
||||
}
|
||||
|
||||
impl<'l> Into<Type<'l>> for &'l TypeT<'l> {
|
||||
fn into(self) -> Type<'l> {
|
||||
Type::Type(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Display, PartialEq, Eq, Hash)]
|
||||
#[display("void")]
|
||||
#[debug("void")]
|
||||
pub struct VoidT<'l> {
|
||||
pub(crate) ctx: Ctx<'l>,
|
||||
}
|
||||
|
||||
impl<'l> Into<Type<'l>> for &'l VoidT<'l> {
|
||||
fn into(self) -> Type<'l> {
|
||||
Type::Void(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Display, PartialEq, Eq, Hash)]
|
||||
#[display("char")]
|
||||
#[debug("char")]
|
||||
pub struct CharT<'l> {
|
||||
pub(crate) ctx: Ctx<'l>,
|
||||
}
|
||||
|
||||
impl<'l> Into<Type<'l>> for &'l CharT<'l> {
|
||||
fn into(self) -> Type<'l> {
|
||||
Type::Char(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Display, PartialEq, Eq, Hash)]
|
||||
#[display("bool")]
|
||||
#[debug("bool")]
|
||||
pub struct BoolT<'l> {
|
||||
pub(crate) ctx: Ctx<'l>,
|
||||
}
|
||||
|
||||
impl<'l> Into<Type<'l>> for &'l BoolT<'l> {
|
||||
fn into(self) -> Type<'l> {
|
||||
Type::Bool(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Display, PartialEq, Eq, Hash)]
|
||||
#[display("const_str")]
|
||||
#[debug("const_str")]
|
||||
pub struct ConstStrT<'l> {
|
||||
pub(crate) ctx: Ctx<'l>,
|
||||
}
|
||||
|
||||
impl<'l> Into<Type<'l>> for &'l ConstStrT<'l> {
|
||||
fn into(self) -> Type<'l> {
|
||||
Type::ConstStr(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Display, PartialEq, Eq, Hash)]
|
||||
#[display("{}{}", if *signed { "i" } else { "u" }, precision)]
|
||||
#[debug("{self}")]
|
||||
pub struct IntT<'l> {
|
||||
pub signed: bool,
|
||||
pub precision: u32,
|
||||
pub(crate) ctx: Ctx<'l>,
|
||||
}
|
||||
|
||||
impl<'l> Into<Type<'l>> for &'l IntT<'l> {
|
||||
fn into(self) -> Type<'l> {
|
||||
Type::Int(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Display, PartialEq, Eq, Hash)]
|
||||
#[display("f{}", precision)]
|
||||
#[debug("{self}")]
|
||||
pub struct FloatT<'l> {
|
||||
pub precision: u32,
|
||||
pub(crate) ctx: Ctx<'l>,
|
||||
}
|
||||
|
||||
impl<'l> Into<Type<'l>> for &'l FloatT<'l> {
|
||||
fn into(self) -> Type<'l> {
|
||||
Type::Float(self)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
pub mod derivations;
|
||||
pub mod intrinsics;
|
||||
|
||||
use crate::{
|
||||
context::Ctx,
|
||||
types::{derivations::*, intrinsics::*},
|
||||
};
|
||||
use derive_more::{Debug, Display};
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Type<'l> {
|
||||
Void(&'l VoidT<'l>),
|
||||
Char(&'l CharT<'l>),
|
||||
Bool(&'l BoolT<'l>),
|
||||
|
||||
#[debug("{_0:?}")]
|
||||
#[display("{_0}")]
|
||||
Int(&'l IntT<'l>),
|
||||
|
||||
#[debug("{_0:?}")]
|
||||
#[display("{_0}")]
|
||||
Float(&'l FloatT<'l>),
|
||||
|
||||
#[debug("{_0:?}")]
|
||||
#[display("{_0}")]
|
||||
Ptr(&'l PtrT<'l>),
|
||||
|
||||
#[debug("{_0:?}")]
|
||||
#[display("{_0}")]
|
||||
Ref(&'l RefT<'l>),
|
||||
|
||||
#[debug("{_0:?}")]
|
||||
#[display("{_0}")]
|
||||
Func(&'l FuncT<'l>),
|
||||
|
||||
#[debug("{_0:?}")]
|
||||
#[display("{_0}")]
|
||||
Type(&'l TypeT<'l>),
|
||||
|
||||
#[debug("{_0:?}")]
|
||||
#[display("{_0}")]
|
||||
ConstStr(&'l ConstStrT<'l>),
|
||||
}
|
||||
|
||||
impl<'l> Type<'l> {
|
||||
pub fn ctx(&self) -> Ctx<'l> {
|
||||
match self {
|
||||
Type::Void(t) => t.ctx,
|
||||
Type::Char(t) => t.ctx,
|
||||
Type::Bool(t) => t.ctx,
|
||||
Type::Int(t) => t.ctx,
|
||||
Type::Float(t) => t.ctx,
|
||||
Type::Ptr(t) => t.base.ctx(),
|
||||
Type::Ref(t) => t.base.ctx(),
|
||||
Type::Func(t) => t.ret_t.ctx(),
|
||||
Type::Type(t) => t.ctx,
|
||||
Type::ConstStr(t) => t.ctx,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
use crate::{context::Ctx, functions::ir::Instruction, types::Type};
|
||||
use bitflags::bitflags;
|
||||
use derive_more::{Debug, Display};
|
||||
use half::f16;
|
||||
use std::hash::Hash;
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ValueFlags: u16 {
|
||||
const Mutable = 0b00000000_00000001;
|
||||
const Volatile = 0b00000000_00000010;
|
||||
const Const = 0b00000001_00000000;
|
||||
const ConstOnly = 0b00000011_00000000;
|
||||
const Type = 0b00000111_00000000;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Int {
|
||||
I8(i8),
|
||||
I16(i16),
|
||||
I32(i32),
|
||||
I64(i64),
|
||||
I128(i128),
|
||||
|
||||
U8(u8),
|
||||
U16(u16),
|
||||
U32(u32),
|
||||
U64(u64),
|
||||
U128(u128),
|
||||
}
|
||||
|
||||
#[derive(Debug, Display, Clone, Copy, PartialEq)]
|
||||
pub enum Float {
|
||||
F16(f16),
|
||||
F32(f32),
|
||||
F64(f64),
|
||||
}
|
||||
|
||||
impl Eq for Float {}
|
||||
|
||||
impl Hash for Float {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
match self {
|
||||
Float::F16(v) => v.to_ne_bytes().hash(state),
|
||||
Float::F32(v) => v.to_ne_bytes().hash(state),
|
||||
Float::F64(v) => v.to_ne_bytes().hash(state),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Const<'l> {
|
||||
#[display("()")]
|
||||
#[debug("()")]
|
||||
Void(Ctx<'l>),
|
||||
|
||||
#[debug("{:?}", _0)]
|
||||
#[display("{}", _0)]
|
||||
Bool(bool, Ctx<'l>),
|
||||
|
||||
#[debug("{:?}", _0)]
|
||||
#[display("{}", _0)]
|
||||
Char(char, Ctx<'l>),
|
||||
|
||||
#[debug("{:?}", _0)]
|
||||
#[display("\"{}\"", _0)]
|
||||
Str(&'l str, Ctx<'l>),
|
||||
|
||||
#[debug("{:?}", _0)]
|
||||
#[display("{}", _0)]
|
||||
Int(Int, Ctx<'l>),
|
||||
|
||||
#[debug("{:?}", _0)]
|
||||
#[display("{}", _0)]
|
||||
Float(Float, Ctx<'l>),
|
||||
}
|
||||
|
||||
impl<'l> Const<'l> {
|
||||
pub fn flags(&self) -> ValueFlags {
|
||||
match self {
|
||||
Const::Void(_) => ValueFlags::Const,
|
||||
Const::Char(_, _) => ValueFlags::Const,
|
||||
Const::Bool(_, _) => ValueFlags::Const,
|
||||
Const::Str(_, _) => ValueFlags::Const,
|
||||
Const::Int(_, _) => ValueFlags::Const,
|
||||
Const::Float(_, _) => ValueFlags::Const,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ty(&self) -> Type<'l> {
|
||||
match self {
|
||||
Const::Void(ctx) => ctx.void_t(),
|
||||
Const::Char(_, ctx) => ctx.char_t(),
|
||||
Const::Bool(_, ctx) => ctx.bool_t(),
|
||||
Const::Str(_, ctx) => ctx.const_str_t(),
|
||||
Const::Int(v, ctx) => match v {
|
||||
Int::I8(_) => ctx.i8_t(),
|
||||
Int::I16(_) => ctx.i16_t(),
|
||||
Int::I32(_) => ctx.i32_t(),
|
||||
Int::I64(_) => ctx.i64_t(),
|
||||
Int::I128(_) => ctx.i128_t(),
|
||||
Int::U8(_) => ctx.u8_t(),
|
||||
Int::U16(_) => ctx.u16_t(),
|
||||
Int::U32(_) => ctx.u32_t(),
|
||||
Int::U64(_) => ctx.u64_t(),
|
||||
Int::U128(_) => ctx.u128_t(),
|
||||
},
|
||||
Const::Float(v, ctx) => match v {
|
||||
Float::F16(_) => ctx.f16_t(),
|
||||
Float::F32(_) => ctx.f32_t(),
|
||||
Float::F64(_) => ctx.f64_t(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'l> Into<Value<'l>> for &'l Const<'l> {
|
||||
fn into(self) -> Value<'l> {
|
||||
Value::Const(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Value<'l> {
|
||||
#[display("{}", _0)]
|
||||
Const(&'l Const<'l>),
|
||||
#[display("{}", _0)]
|
||||
Type(Type<'l>),
|
||||
#[display("%{}", _0.id())]
|
||||
Instruction(&'l Instruction<'l>),
|
||||
}
|
||||
|
||||
impl<'l> Value<'l> {
|
||||
pub fn flags(&self) -> ValueFlags {
|
||||
match self {
|
||||
Value::Const(c) => c.flags(),
|
||||
Value::Type(_) => ValueFlags::Type,
|
||||
Value::Instruction(i) => i.value_flags(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ty(&self) -> Type<'l> {
|
||||
match self {
|
||||
Value::Const(v) => v.ty(),
|
||||
Value::Type(v) => v.ctx().type_t(),
|
||||
Value::Instruction(v) => v.value_ty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user