Ifs and Phis

This commit is contained in:
Mia
2026-03-07 16:24:32 +01:00
parent fb84e09391
commit 168a12b4fc
11 changed files with 612 additions and 150 deletions
+156 -26
View File
@@ -5,7 +5,13 @@ use crate::{
values::{AnyConst, AnyValue, Value, ValueFlags, default_associated_values},
};
use derive_more::{Debug, Display};
use std::{borrow::Cow, cell::UnsafeCell, hash::Hash, ops::Deref, sync::OnceLock};
use std::{
borrow::Cow,
cell::UnsafeCell,
hash::Hash,
ops::Deref,
sync::{OnceLock, atomic::AtomicU16},
};
// Maybe unsafe but honestly it's extremely unlikely that this will go wrong and it won't cause any issues.
struct Id(UnsafeCell<u32>);
@@ -17,6 +23,8 @@ unsafe impl Sync for Id {}
#[display("%{}", unsafe { *id.0.get() })]
pub struct Instruction<'l> {
id: Id,
ty: OnceLock<Type<'l>>,
flags: AtomicU16,
pub parent_block: &'l Block<'l>,
pub variant: InstructionVariant<'l>,
}
@@ -56,11 +64,19 @@ impl<'l> Instruction<'l> {
pub fn id(&self) -> u32 {
unsafe { *self.id.0.get() }
}
#[inline]
pub unsafe fn edit_flags(&self, edit: impl FnOnce(ValueFlags) -> ValueFlags) {
let flags = self.flags.load(std::sync::atomic::Ordering::Relaxed);
let flags = edit(ValueFlags::from_bits_retain(flags));
self.flags
.store(flags.bits(), std::sync::atomic::Ordering::Relaxed);
}
}
impl<'l> Value<'l> for &'l Instruction<'l> {
fn ty(&self) -> Type<'l> {
match self.variant {
*self.ty.get_or_init(|| match self.variant {
InstructionVariant::Return(_) => Type::Void,
InstructionVariant::Store(_, _) => Type::Void,
InstructionVariant::StackAlloc(ty) => ty.make_ptr(true).into(),
@@ -68,7 +84,7 @@ impl<'l> Value<'l> for &'l Instruction<'l> {
Type::Ptr(PtrT { base, .. }) => *base,
_ => unreachable!(),
},
InstructionVariant::GetElementPtr(v, _) => match v.ty() {
InstructionVariant::GetElementPtr(v, i) => match v.ty() {
Type::Ptr(PtrT {
base: Type::Array(ArrayT { base, .. }),
mutable,
@@ -77,6 +93,26 @@ impl<'l> Value<'l> for &'l Instruction<'l> {
base: Type::Array(ArrayT { base, .. }),
mutable,
}) => base.make_ref(*mutable).into(),
Type::Ptr(PtrT {
base: Type::Struct(StructT { fields, .. }),
mutable,
}) => {
let fields = fields.get().unwrap();
let AnyValue::Constant(AnyConst::Str(name)) = i else {
unreachable!();
};
fields[name].ty.make_ptr(*mutable).into()
}
Type::Ref(RefT {
base: Type::Struct(StructT { fields, .. }),
mutable,
}) => {
let fields = fields.get().unwrap();
let AnyValue::Constant(AnyConst::Str(name)) = i else {
unreachable!();
};
fields[name].ty.make_ref(*mutable).into()
}
_ => unreachable!(),
},
InstructionVariant::GetElementVal(v, i) => match v.ty() {
@@ -108,18 +144,13 @@ impl<'l> Value<'l> for &'l Instruction<'l> {
InstructionVariant::Call(f, _) => f.ty.ret_t,
InstructionVariant::Jump(_) => Type::Void,
InstructionVariant::Branch { .. } => Type::Void,
InstructionVariant::Reinterpret(_, t, _) => t,
InstructionVariant::Reinterpret(_, t) => t,
_ => todo!("{self:?}"),
}
})
}
fn flags(&self) -> ValueFlags {
match self.variant {
InstructionVariant::StackAlloc(_) => ValueFlags::LValue,
InstructionVariant::GetElementPtr(v, _) if v.is_lvalue() => ValueFlags::LValue,
InstructionVariant::Reinterpret(_, _, f) => f,
_ => ValueFlags::empty(),
}
ValueFlags::from_bits_retain(self.flags.load(std::sync::atomic::Ordering::Relaxed))
}
fn get_associated_value(&self, name: &str) -> Option<AnyValue<'l>> {
@@ -141,6 +172,13 @@ pub enum Cmp {
Ge,
}
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Hash)]
#[display("[{value}, #{block}]")]
pub struct PhiValue<'l> {
pub block: u32,
pub value: AnyValue<'l>,
}
#[derive(PartialEq, Eq)]
pub enum InstructionVariant<'l> {
StackAlloc(Type<'l>),
@@ -171,6 +209,7 @@ pub enum InstructionVariant<'l> {
FCmp(AnyValue<'l>, AnyValue<'l>, Cmp),
MakeStruct(&'l StructT<'l>, &'l [AnyValue<'l>]),
Phi(&'l [PhiValue<'l>]),
Call(&'l Function<'l>, Vec<AnyValue<'l>>),
Jump(&'l Block<'l>),
@@ -181,7 +220,7 @@ pub enum InstructionVariant<'l> {
},
Return(Option<AnyValue<'l>>),
Reinterpret(AnyValue<'l>, Type<'l>, ValueFlags),
Reinterpret(AnyValue<'l>, Type<'l>),
}
impl InstructionVariant<'_> {
@@ -217,6 +256,15 @@ impl std::fmt::Debug for InstructionVariant<'_> {
Self::ICmp(a, b, c) => write!(f, "icmp {c:?} {a}, {b}"),
Self::FCmp(a, b, c) => write!(f, "fcmp {c:?} {a}, {b}"),
Self::Phi(values) => {
write!(f, "phi")?;
let mut separator = " ";
for val in *values {
write!(f, "{separator}{val}")?;
separator = ", ";
}
Ok(())
}
Self::MakeStruct(ty, vals) => {
write!(
f,
@@ -258,7 +306,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:?}"),
Self::Reinterpret(v, t) => write!(f, "reinterpret {v} as {t}"),
}
}
}
@@ -288,7 +336,7 @@ impl PartialEq for Block<'_> {
}
pub struct BlockBuilder<'l> {
block: &'l Block<'l>,
pub block: &'l Block<'l>,
instructions: Vec<&'l Instruction<'l>>,
}
@@ -298,6 +346,10 @@ pub type BlockBuilderResult<'l, T> = Result<T, BlockBuilderError<'l>>;
impl<'l> BlockBuilder<'l> {
pub fn stack_alloc(&mut self, ty: Type<'l>) -> BlockBuilderResult<'l, AnyValue<'l>> {
let inst = self.push_instruction(InstructionVariant::StackAlloc(ty))?;
inst.flags.store(
ValueFlags::LValue.bits(),
std::sync::atomic::Ordering::Relaxed,
);
Ok(inst.into())
}
@@ -477,6 +529,24 @@ impl<'l> BlockBuilder<'l> {
}
}
pub fn ptr_to_int(
&mut self,
val: AnyValue<'l>,
target: IntT,
) -> BlockBuilderResult<'l, AnyValue<'l>> {
let ty = val.ty();
match ty {
Type::Ptr(_) => {
let inst = self.push_instruction(InstructionVariant::PtrToInt(val, target))?;
Ok(inst.into())
}
_ => Err(format!(
"Cannot convert non-pointer value of type `{ty}` to integer `{target}`."
)
.into()),
}
}
pub fn cmp(
&mut self,
a: AnyValue<'l>,
@@ -506,23 +576,48 @@ impl<'l> BlockBuilder<'l> {
let v_ty = value.ty();
let i_ty = index.ty();
let _ = match v_ty {
match v_ty {
Type::Ptr(PtrT {
base: Type::Array(ArrayT { base, .. }),
base: Type::Array(ArrayT { .. }),
..
}) => *base,
Type::Ref(RefT {
base: Type::Array(ArrayT { base, .. }),
})
| Type::Ref(RefT {
base: Type::Array(ArrayT { .. }),
..
}) => *base,
}) => {
if i_ty != Type::USIZE {
return Err(format!("Expeted index type `usize`, found {i_ty}.").into());
}
}
Type::Ptr(PtrT {
base: Type::Struct(StructT { fields, .. }),
..
})
| Type::Ref(RefT {
base: Type::Struct(StructT { fields, .. }),
..
}) => {
if i_ty != Type::ConstStr {
return Err(format!("Expeted index type `const str`, found {i_ty}.").into());
}
let AnyValue::Constant(AnyConst::Str(name)) = index else {
unreachable!();
};
let fields = fields.get().unwrap();
if !fields.contains_key(name) {
return Err(format!("Type `{v_ty}` does not contain field `{name}`.").into());
}
}
_ => 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))?;
if value.is_lvalue() {
inst.flags.store(
ValueFlags::LValue.bits(),
std::sync::atomic::Ordering::Relaxed,
);
}
Ok(inst.into())
}
@@ -571,7 +666,7 @@ impl<'l> BlockBuilder<'l> {
.find(|(_, (a, b))| a.ty != b.ty())
{
return Err(format!(
"Invalid valua at position {i}. Expected type `{}`, found `{}`.",
"Invalid value at position {i}. Expected type `{}`, found `{}`.",
a.ty,
b.ty()
)
@@ -581,6 +676,22 @@ impl<'l> BlockBuilder<'l> {
return Ok(inst.into());
}
pub fn phi(
&mut self,
values: impl ExactSizeIterator<Item = PhiValue<'l>>,
) -> BlockBuilderResult<'l, AnyValue<'l>> {
if values.len() < 2 {
return Err("Instruction requires at least two values.".into());
}
let values = self.block.func.ctx().alloc.alloc_slice(values);
let ty = values[0].value.ty();
if values.iter().any(|v| v.value.ty() != ty) {
return Err("All values must have the same type.".into());
}
let inst = self.push_instruction(InstructionVariant::Phi(values))?;
Ok(inst.into())
}
pub fn jump(&mut self, block: &'l Block<'l>) -> BlockBuilderResult<'l, AnyValue<'l>> {
if !std::ptr::eq(block.func, self.block.func) {
return Err("Block does not belong to this function.".into());
@@ -667,7 +778,9 @@ impl<'l> BlockBuilder<'l> {
ty: Type<'l>,
flags: ValueFlags,
) -> BlockBuilderResult<'l, AnyValue<'l>> {
let inst = self.push_instruction(InstructionVariant::Reinterpret(value, ty, flags))?;
let inst = self.push_instruction(InstructionVariant::Reinterpret(value, ty))?;
inst.flags
.store(flags.bits(), std::sync::atomic::Ordering::Relaxed);
Ok(inst.into())
}
@@ -697,6 +810,8 @@ impl<'l> BlockBuilder<'l> {
let instruction = &*self.block.func.ctx().alloc.alloc(Instruction {
id: Id(UnsafeCell::new(u32::MAX)),
parent_block: self.block,
ty: OnceLock::new(),
flags: AtomicU16::new(0),
variant,
});
self.instructions.push(instruction);
@@ -841,6 +956,14 @@ impl<'l> FunctionBodyBuilder<'l> {
self.current_builder().int_to_ptr(val, target)
}
pub fn ptr_to_int(
&mut self,
val: AnyValue<'l>,
target: IntT,
) -> BlockBuilderResult<'l, AnyValue<'l>> {
self.current_builder().ptr_to_int(val, target)
}
pub fn cmp(
&mut self,
a: AnyValue<'l>,
@@ -874,6 +997,13 @@ impl<'l> FunctionBodyBuilder<'l> {
self.current_builder().make_struct(struct_ty, values)
}
pub fn phi(
&mut self,
values: impl ExactSizeIterator<Item = PhiValue<'l>>,
) -> BlockBuilderResult<'l, AnyValue<'l>> {
self.current_builder().phi(values)
}
pub fn jump(&mut self, block: &'l Block<'l>) -> BlockBuilderResult<'l, AnyValue<'l>> {
self.current_builder().jump(block)
}
+29 -12
View File
@@ -49,6 +49,23 @@ impl<'l> Value<'l> for IntT {
}
}
#[rustfmt::skip]
impl IntT {
pub const I8: Self = IntT { signed: true, precision: 8 };
pub const I16: Self = IntT { signed: true, precision: 16 };
pub const I32: Self = IntT { signed: true, precision: 32 };
pub const I64: Self = IntT { signed: true, precision: 64 };
pub const I128: Self = IntT { signed: true, precision: 128 };
pub const ISIZE: Self = IntT { signed: true, precision: u32::MAX };
pub const U8: Self = IntT { signed: false, precision: 8 };
pub const U16: Self = IntT { signed: false, precision: 16 };
pub const U32: Self = IntT { signed: false, precision: 32 };
pub const U64: Self = IntT { signed: false, precision: 64 };
pub const U128: Self = IntT { signed: false, precision: 128 };
pub const USIZE: Self = IntT { signed: false, precision: u32::MAX };
}
#[non_exhaustive]
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Hash)]
#[display("f{}", precision)]
@@ -198,19 +215,19 @@ impl<'l> Value<'l> for Type<'l> {
#[rustfmt::skip]
impl Type<'_> {
pub const I8: Self = Type::Int(IntT { signed: true, precision: 8 });
pub const I16: Self = Type::Int(IntT { signed: true, precision: 16 });
pub const I32: Self = Type::Int(IntT { signed: true, precision: 32 });
pub const I64: Self = Type::Int(IntT { signed: true, precision: 64 });
pub const I128: Self = Type::Int(IntT { signed: true, precision: 128 });
pub const ISIZE: Self = Type::Int(IntT { signed: true, precision: u32::MAX });
pub const I8: Self = Type::Int(IntT::I8);
pub const I16: Self = Type::Int(IntT::I16);
pub const I32: Self = Type::Int(IntT::I32);
pub const I64: Self = Type::Int(IntT::I64);
pub const I128: Self = Type::Int(IntT::I128);
pub const ISIZE: Self = Type::Int(IntT::ISIZE);
pub const U8: Self = Type::Int(IntT { signed: false, precision: 8 });
pub const U16: Self = Type::Int(IntT { signed: false, precision: 16 });
pub const U32: Self = Type::Int(IntT { signed: false, precision: 32 });
pub const U64: Self = Type::Int(IntT { signed: false, precision: 64 });
pub const U128: Self = Type::Int(IntT { signed: false, precision: 128 });
pub const USIZE: Self = Type::Int(IntT { signed: false, precision: u32::MAX });
pub const U8: Self = Type::Int(IntT::U8);
pub const U16: Self = Type::Int(IntT::U16);
pub const U32: Self = Type::Int(IntT::U32);
pub const U64: Self = Type::Int(IntT::U64);
pub const U128: Self = Type::Int(IntT::U128);
pub const USIZE: Self = Type::Int(IntT::USIZE);
pub const F16: Self = Type::Float(FloatT { precision: 16 });
pub const F32: Self = Type::Float(FloatT { precision: 32 });
+6
View File
@@ -176,6 +176,10 @@ pub enum AnyConst<'l> {
#[display("{}", StructDisplay(_0, _1))]
Struct(&'l StructT<'l>, &'l [AnyConst<'l>]),
Type(Type<'l>),
#[from(ignore)]
#[debug("SizeOf({:?})", _0)]
#[display("size_of({_0})")]
SizeOf(Type<'l>),
}
impl<'l> Value<'l> for AnyConst<'l> {
@@ -194,6 +198,7 @@ impl<'l> Value<'l> for AnyConst<'l> {
Self::Array(a @ [v, ..]) => Type::Array(v.ty().make_array(Some(a.len() as u32))),
Self::Str(s) => s.ty(),
Self::Struct(t, _) => Type::Struct(t),
Self::SizeOf(_) => Type::USIZE,
_ => todo!("{self:?}"),
}
}
@@ -218,6 +223,7 @@ impl<'l> Value<'l> for AnyConst<'l> {
AnyConst::Array(_) => default_associated_values(self, name),
AnyConst::Struct(t, _) => t.get_associated_value(name),
AnyConst::Type(t) => t.get_associated_value(name),
AnyConst::SizeOf(_) => Type::USIZE.get_associated_value(name),
}
}
+1 -1
View File
@@ -77,7 +77,7 @@ impl<'l> Value<'l> for AnyValue<'l> {
fn get_associated_value(&self, name: &str) -> Option<AnyValue<'l>> {
match self {
AnyValue::Constant(v) => v.get_associated_value(name),
AnyValue::Instruction(v) => todo!(),
AnyValue::Instruction(_) => default_associated_values(self, name),
AnyValue::Parameter(_, _) => default_associated_values(self, name),
}
}