Casting, GEPs, partial LLVM backend

This commit is contained in:
Mia
2026-03-06 15:21:44 +01:00
parent 0b4f169c6f
commit bd0b619127
27 changed files with 1260 additions and 947 deletions
+20 -5
View File
@@ -1,16 +1,18 @@
use crate::{
functions::Function,
types::derivations::{FuncT, TypeDerivations},
values::Const,
};
use derive_more::Debug;
use derive_more::{Debug, Display};
use fxhash::FxBuildHasher;
use leaf_allocators::SyncAllocator;
use scc::HashMap;
use std::{borrow::Cow, hash::Hash, sync::OnceLock};
use std::{borrow::Cow, fmt::Display, hash::Hash, sync::OnceLock};
#[derive(derive_more::Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(
derive_more::Debug, Display, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash,
)]
#[debug("{major}.{minor}.{patch}.{build}")]
#[display("{major}.{minor}.{patch}.{build}")]
pub struct Version {
pub major: u16,
pub minor: u16,
@@ -18,7 +20,8 @@ pub struct Version {
pub build: u16,
}
#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Debug, Display, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[display("{name} v{version}")]
pub struct AssemblyIdentifier {
pub version: Version,
pub name: Cow<'static, str>,
@@ -57,6 +60,10 @@ impl<'l> Context<'l> {
})
}
pub fn alloc(&self) -> &'l dyn SyncAllocator {
self.alloc
}
pub fn get_assembly(&'l self, ident: &AssemblyIdentifier) -> Option<&'l Assembly<'l>> {
self.assemblies.get_sync(ident).map(|v| *v)
}
@@ -114,6 +121,10 @@ impl<'l> Assembly<'l> {
self.ctx
}
pub fn ident(&self) -> &AssemblyIdentifier {
&self.ident
}
pub fn create_function(&'l self, ty: &'l FuncT<'l>) -> &'l Function<'l> {
let func = self.ctx.alloc.alloc(Function {
ty,
@@ -125,6 +136,10 @@ impl<'l> Assembly<'l> {
func
}
pub fn functions(&'l self) -> boxcar::vec::Iter<'l, &'l Function<'l>> {
self.functions.iter()
}
pub fn find_function(
&'l self,
filter: impl Fn(&'l Function<'l>) -> bool,
+192 -7
View File
@@ -1,7 +1,7 @@
use crate::{
assembly::Ctx,
functions::{Function, FunctionBody},
types::{Type, derivations::*},
types::{IntT, Type, derivations::*},
values::{Value, ValueFlags},
};
use derive_more::{Debug, Display};
@@ -25,15 +25,15 @@ impl Eq for Instruction<'_> {}
impl PartialEq for Instruction<'_> {
#[inline]
fn eq(&self, _: &Self) -> bool {
false
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(self, other)
}
}
impl Hash for Instruction<'_> {
#[inline]
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
(self as *const Self).hash(state);
std::ptr::hash(self, state);
}
}
@@ -60,6 +60,10 @@ impl<'l> Instruction<'l> {
pub fn value_flags(&self) -> ValueFlags {
match self.variant {
InstructionVariant::StackAlloc(_) => ValueFlags::LValue,
InstructionVariant::GetElementPtr(v, _) if v.flags().contains(ValueFlags::LValue) => {
ValueFlags::LValue
}
InstructionVariant::Reinterpret(_, _, f) => f,
_ => ValueFlags::empty(),
}
}
@@ -73,12 +77,35 @@ impl<'l> Instruction<'l> {
Type::Ptr(PtrT { base, .. }) => *base,
_ => unreachable!(),
},
InstructionVariant::GetElementPtr(v, _) => match v.ty() {
Type::Ptr(PtrT {
base: Type::Array(ArrayT { base, .. }),
mutable,
}) => base.make_ptr(*mutable).into(),
Type::Ref(RefT {
base: Type::Array(ArrayT { base, .. }),
mutable,
}) => base.make_ref(*mutable).into(),
_ => unreachable!(),
},
InstructionVariant::IAdd(a, _) => a.ty(),
InstructionVariant::ISub(a, _) => a.ty(),
InstructionVariant::IMul(a, _) => a.ty(),
InstructionVariant::IDiv(a, _) => a.ty(),
InstructionVariant::IMod(a, _) => a.ty(),
InstructionVariant::SExt(_, t) => Type::Int(t),
InstructionVariant::ZExt(_, t) => Type::Int(t),
InstructionVariant::Trunc(_, t) => Type::Int(t),
InstructionVariant::FAdd(a, _) => a.ty(),
InstructionVariant::FSub(a, _) => a.ty(),
InstructionVariant::FMul(a, _) => a.ty(),
InstructionVariant::FDiv(a, _) => a.ty(),
InstructionVariant::FMod(a, _) => a.ty(),
InstructionVariant::ICmp(_, _, _) => Type::Bool,
InstructionVariant::Call(f, _) => f.ty.ret_t,
InstructionVariant::Jump(_) => Type::Void,
InstructionVariant::Branch { .. } => Type::Void,
InstructionVariant::Reinterpret(_, t, _) => t,
_ => todo!("{self:?}"),
}
}
@@ -87,6 +114,7 @@ impl<'l> Instruction<'l> {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Cmp {
Eq,
Ne,
Lt,
Gt,
Le,
@@ -100,11 +128,21 @@ pub enum InstructionVariant<'l> {
Load(Value<'l>),
Store(Value<'l>, Value<'l>),
GetElementPtr(Value<'l>, Value<'l>),
IAdd(Value<'l>, Value<'l>),
ISub(Value<'l>, Value<'l>),
IMul(Value<'l>, Value<'l>),
IDiv(Value<'l>, Value<'l>),
IMod(Value<'l>, Value<'l>),
SExt(Value<'l>, IntT),
ZExt(Value<'l>, IntT),
Trunc(Value<'l>, IntT),
FAdd(Value<'l>, Value<'l>),
FSub(Value<'l>, Value<'l>),
FMul(Value<'l>, Value<'l>),
FDiv(Value<'l>, Value<'l>),
FMod(Value<'l>, Value<'l>),
ICmp(Value<'l>, Value<'l>, Cmp),
FCmp(Value<'l>, Value<'l>, Cmp),
@@ -117,6 +155,8 @@ pub enum InstructionVariant<'l> {
false_case: &'l Block<'l>,
},
Return(Option<Value<'l>>),
Reinterpret(Value<'l>, Type<'l>, ValueFlags),
}
impl InstructionVariant<'_> {
@@ -137,10 +177,20 @@ impl std::fmt::Debug for InstructionVariant<'_> {
Self::GCAlloc(ty) => write!(f, "gcalloc {ty}"),
Self::Load(v) => write!(f, "load {v}"),
Self::Store(t, v) => write!(f, "store {t}, {v}"),
Self::GetElementPtr(t, v) => write!(f, "gep {t}, {v}"),
Self::IAdd(a, b) => write!(f, "iadd {a}, {b}"),
Self::ISub(a, b) => write!(f, "isub {a}, {b}"),
Self::IMul(a, b) => write!(f, "imul {a}, {b}"),
Self::IDiv(a, b) => write!(f, "idiv {a}, {b}"),
Self::IMod(a, b) => write!(f, "imod {a}, {b}"),
Self::SExt(a, b) => write!(f, "sext {a}, {b}"),
Self::ZExt(a, b) => write!(f, "zext {a}, {b}"),
Self::Trunc(a, b) => write!(f, "trunc {a}, {b}"),
Self::FAdd(a, b) => write!(f, "fadd {a}, {b}"),
Self::FSub(a, b) => write!(f, "fsub {a}, {b}"),
Self::FMul(a, b) => write!(f, "fmul {a}, {b}"),
Self::FDiv(a, b) => write!(f, "fdiv {a}, {b}"),
Self::FMod(a, b) => write!(f, "fmod {a}, {b}"),
Self::ICmp(a, b, c) => write!(f, "icmp {c:?} {a}, {b}"),
Self::FCmp(a, b, c) => write!(f, "fcmp {c:?} {a}, {b}"),
Self::Call(func, args) => {
@@ -164,6 +214,7 @@ impl std::fmt::Debug for InstructionVariant<'_> {
Self::Jump(b) => write!(f, "jump #{}", b.id),
Self::Return(None) => write!(f, "return"),
Self::Return(Some(v)) => write!(f, "return {v}"),
Self::Reinterpret(v, t, fl) => write!(f, "reinterpret {v} as {t} {fl:?}"),
}
}
}
@@ -260,7 +311,23 @@ impl<'l> BlockBuilder<'l> {
let inst = self.push_instruction(InstructionVariant::FAdd(a, b))?;
Ok(inst.into())
}
_ => Err(format!("Cannot add values of type `{a_ty}` and `b_ty`.").into()),
_ => Err(format!("Cannot add values of type `{a_ty}` and `{b_ty}`.").into()),
}
}
pub fn sub(&mut self, a: Value<'l>, b: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
let [a_ty, b_ty] = [a.ty(), b.ty()];
match (a_ty, b_ty) {
(Type::Int(a_ty), Type::Int(b_ty)) if a_ty == b_ty => {
let inst = self.push_instruction(InstructionVariant::ISub(a, b))?;
Ok(inst.into())
}
(Type::Float(a_ty), Type::Float(b_ty)) if a_ty == b_ty => {
let inst = self.push_instruction(InstructionVariant::FSub(a, b))?;
Ok(inst.into())
}
_ => Err(format!("Cannot subtract values of type `{a_ty}` and `{b_ty}`.").into()),
}
}
@@ -276,7 +343,52 @@ impl<'l> BlockBuilder<'l> {
let inst = self.push_instruction(InstructionVariant::FMul(a, b))?;
Ok(inst.into())
}
_ => Err(format!("Cannot add values of type `{a_ty}` and `b_ty`.").into()),
_ => Err(format!("Cannot multiply values of type `{a_ty}` and `{b_ty}`.").into()),
}
}
pub fn div(&mut self, a: Value<'l>, b: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
let [a_ty, b_ty] = [a.ty(), b.ty()];
match (a_ty, b_ty) {
(Type::Int(a_ty), Type::Int(b_ty)) if a_ty == b_ty => {
let inst = self.push_instruction(InstructionVariant::IDiv(a, b))?;
Ok(inst.into())
}
(Type::Float(a_ty), Type::Float(b_ty)) if a_ty == b_ty => {
let inst = self.push_instruction(InstructionVariant::FDiv(a, b))?;
Ok(inst.into())
}
_ => Err(format!("Cannot divide values of type `{a_ty}` and `{b_ty}`.").into()),
}
}
pub fn modulo(&mut self, a: Value<'l>, b: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
let [a_ty, b_ty] = [a.ty(), b.ty()];
match (a_ty, b_ty) {
(Type::Int(a_ty), Type::Int(b_ty)) if a_ty == b_ty => {
let inst = self.push_instruction(InstructionVariant::IMod(a, b))?;
Ok(inst.into())
}
(Type::Float(a_ty), Type::Float(b_ty)) if a_ty == b_ty => {
let inst = self.push_instruction(InstructionVariant::FMod(a, b))?;
Ok(inst.into())
}
_ => Err(format!("Cannot divide values of type `{a_ty}` and `{b_ty}`.").into()),
}
}
pub fn trunc(&mut self, val: Value<'l>, target: IntT) -> BlockBuilderResult<'l, Value<'l>> {
let ty = val.ty();
match ty {
Type::Int(a_ty) if a_ty.precision > target.precision => {
let inst = self.push_instruction(InstructionVariant::Trunc(val, target))?;
Ok(inst.into())
}
_ => Err(
format!("Cannot truncate value of type `{ty}` to one of type `{target}`.").into(),
),
}
}
@@ -297,10 +409,34 @@ impl<'l> BlockBuilder<'l> {
let inst = self.push_instruction(InstructionVariant::FCmp(a, b, cmp))?;
Ok(inst.into())
}
_ => Err(format!("Cannot compare values of type `{a_ty}` and `b_ty`.").into()),
_ => Err(format!("Cannot compare values of type `{a_ty}` and `{b_ty}`.").into()),
}
}
pub fn gep(&mut self, value: Value<'l>, index: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
let v_ty = value.ty();
let i_ty = index.ty();
let _ = match v_ty {
Type::Ptr(PtrT {
base: Type::Array(ArrayT { base, .. }),
..
}) => *base,
Type::Ref(RefT {
base: Type::Array(ArrayT { base, .. }),
..
}) => *base,
_ => return Err(format!("Cannot index a value of type `{}`.", v_ty).into()),
};
if i_ty != Type::USIZE {
return Err(format!("Expeted index type `usize`, found {}.", i_ty).into());
}
let inst = self.push_instruction(InstructionVariant::GetElementPtr(value, index))?;
Ok(inst.into())
}
pub fn jump(&mut self, block: &'l Block<'l>) -> BlockBuilderResult<'l, Value<'l>> {
if !std::ptr::eq(block.func, self.block.func) {
return Err("Block does not belong to this function.".into());
@@ -376,6 +512,21 @@ impl<'l> BlockBuilder<'l> {
Ok(inst.into())
}
/// WARNING: Incorrect usage of this function is very likely to cause catastrophic problems. Be sure to know what you're doing.
///
/// # Safety
///
/// The target reinterpretation must maintain the IR's invariants.
pub unsafe fn reinterpret(
&mut self,
value: Value<'l>,
ty: Type<'l>,
flags: ValueFlags,
) -> BlockBuilderResult<'l, Value<'l>> {
let inst = self.push_instruction(InstructionVariant::Reinterpret(value, ty, flags))?;
Ok(inst.into())
}
#[inline]
pub fn has_termination(&self) -> bool {
match self.instructions.as_slice() {
@@ -494,10 +645,26 @@ impl<'l> FunctionBodyBuilder<'l> {
self.current_builder().add(a, b)
}
pub fn sub(&mut self, a: Value<'l>, b: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
self.current_builder().sub(a, b)
}
pub fn mul(&mut self, a: Value<'l>, b: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
self.current_builder().mul(a, b)
}
pub fn div(&mut self, a: Value<'l>, b: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
self.current_builder().div(a, b)
}
pub fn modulo(&mut self, a: Value<'l>, b: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
self.current_builder().modulo(a, b)
}
pub fn trunc(&mut self, val: Value<'l>, target: IntT) -> BlockBuilderResult<'l, Value<'l>> {
self.current_builder().trunc(val, target)
}
pub fn cmp(
&mut self,
a: Value<'l>,
@@ -507,6 +674,10 @@ impl<'l> FunctionBodyBuilder<'l> {
self.current_builder().cmp(a, b, cmp)
}
pub fn gep(&mut self, value: Value<'l>, index: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
self.current_builder().gep(value, index)
}
pub fn jump(&mut self, block: &'l Block<'l>) -> BlockBuilderResult<'l, Value<'l>> {
self.current_builder().jump(block)
}
@@ -532,6 +703,20 @@ impl<'l> FunctionBodyBuilder<'l> {
self.current_builder().ret(value)
}
/// WARNING: Incorrect usage of this function is very likely to cause catastrophic problems. Be sure to know what you're doing.
///
/// # Safety
///
/// The target reinterpretation must maintain the IR's invariants.
pub unsafe fn reinterpret(
&mut self,
value: Value<'l>,
ty: Type<'l>,
flags: ValueFlags,
) -> BlockBuilderResult<'l, Value<'l>> {
unsafe { self.current_builder().reinterpret(value, ty, flags) }
}
fn current_builder(&mut self) -> &mut BlockBuilder<'l> {
&mut self.blocks[self.current_block]
}
+4
View File
@@ -24,6 +24,10 @@ impl<'l> Function<'l> {
self.declaring_assembly.ctx()
}
pub fn declaring_assembly(&self) -> &'l Assembly<'l> {
self.declaring_assembly
}
pub fn body(&self) -> Option<&FunctionBody<'l>> {
self.body.get()
}
+34
View File
@@ -8,6 +8,7 @@ pub struct TypeDerivations<'l> {
alloc: &'l dyn SyncAllocator,
ptr_t: HashMap<(Type<'l>, bool), Type<'l>>,
ref_t: HashMap<(Type<'l>, bool), Type<'l>>,
arr_t: HashMap<(Type<'l>, Option<u32>), Type<'l>>,
fun_t: HashMap<(Type<'l>, Arc<[Type<'l>]>), Type<'l>>,
}
@@ -17,6 +18,7 @@ impl<'l> TypeDerivations<'l> {
alloc,
ptr_t: HashMap::new(),
ref_t: HashMap::new(),
arr_t: HashMap::new(),
fun_t: HashMap::new(),
}
}
@@ -43,6 +45,17 @@ impl<'l> TypeDerivations<'l> {
ty
}
pub fn make_array(&self, base: Type<'l>, length: Option<u32>) -> &'l ArrayT<'l> {
let Type::Array(ty) = *self
.arr_t
.entry_sync((base, length))
.or_insert_with(|| (&*self.alloc.alloc(ArrayT { base, length })).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
@@ -95,6 +108,23 @@ pub struct RefT<'l> {
pub mutable: bool,
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ArrayT<'l> {
#[debug("{base}")]
pub base: Type<'l>,
pub length: Option<u32>,
}
impl Display for ArrayT<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.length {
None => write!(f, "[{}]", self.base),
Some(len) => write!(f, "[{}; {len}]", self.base),
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FuncT<'l> {
@@ -124,6 +154,10 @@ impl<'l> Type<'l> {
self.ctx().derivations.make_ref(*self, mutable)
}
pub fn make_array(&self, length: Option<u32>) -> &'l ArrayT<'l> {
self.ctx().derivations.make_array(*self, length)
}
pub fn make_fn(&self, par_t: impl IntoIterator<Item = Type<'l>>) -> &'l FuncT<'l> {
self.ctx()
.derivations
+7 -2
View File
@@ -51,6 +51,10 @@ pub enum Type<'l> {
#[display("{_0}")]
Ref(&'l RefT<'l>),
#[debug("{_0:?}")]
#[display("{_0}")]
Array(&'l ArrayT<'l>),
#[debug("{_0:?}")]
#[display("{_0}")]
Func(&'l FuncT<'l>),
@@ -82,8 +86,9 @@ impl<'l> Type<'l> {
Type::ConstStr => None,
Type::Int(_) => None,
Type::Float(_) => None,
Type::Ptr(_) => None,
Type::Ref(_) => None,
Type::Ptr(PtrT { base, .. }) => base.non_default_ctx(),
Type::Ref(RefT { base, .. }) => base.non_default_ctx(),
Type::Array(ArrayT { base, .. }) => base.non_default_ctx(),
Type::Func(f) => match f.ret_t.non_default_ctx() {
Some(ctx) => Some(ctx),
None => f.par_t.iter().find_map(|t| t.non_default_ctx()),
+25 -1
View File
@@ -1,4 +1,8 @@
use crate::{functions::Function, types::Type, values::ValueFlags};
use crate::{
functions::Function,
types::{Type, derivations::ArrayT},
values::ValueFlags,
};
use derive_more::{Debug, *};
use half::f16;
use std::hash::Hash;
@@ -55,6 +59,18 @@ where
}
}
struct ListDisplay<'l>(&'l [AnyConst<'l>]);
impl std::fmt::Display for ListDisplay<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut list = f.debug_list();
for ele in self.0 {
list.entry(&format_args!("{ele}"));
}
list.finish()
}
}
#[derive(Debug, Display, Clone, Copy, From, TryInto, PartialEq, Eq, Hash)]
#[from(forward)]
pub enum AnyConst<'l> {
@@ -79,6 +95,9 @@ pub enum AnyConst<'l> {
#[debug("{:?}", _0)]
#[display("{}", _0)]
Function(&'l Function<'l>),
#[debug("{:?}", _0)]
#[display("{}", ListDisplay(_0))]
Array(&'l [AnyConst<'l>]),
Type(Type<'l>),
}
@@ -104,6 +123,11 @@ impl<'l> AnyConst<'l> {
Self::Float(Float::F16(_)) => Type::F16,
Self::Float(Float::F32(_)) => Type::F32,
Self::Float(Float::F64(_)) => Type::F64,
Self::Array([]) => Type::Array(&ArrayT {
base: Type::Void,
length: Some(0),
}),
Self::Array(a @ [v, ..]) => Type::Array(v.ty().make_array(Some(a.len() as u32))),
_ => todo!("{self:?}"),
}
}
+1 -1
View File
@@ -53,7 +53,7 @@ impl<'l> Value<'l> {
pub fn flags(&self) -> ValueFlags {
match self {
Value::Instruction(instruction) => instruction.value_flags(),
Value::Instruction(v) => v.value_flags(),
Value::Parameter(_, _) => ValueFlags::empty(),
Value::Constant(c) => c.flags(),
_ => todo!("{self:?}"),