Ifs and Phis
This commit is contained in:
+156
-26
@@ -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
@@ -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 });
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user