920 lines
24 KiB
Rust
920 lines
24 KiB
Rust
use crate::{
|
|
assembly::Ctx,
|
|
functions::{Function, FunctionBody},
|
|
types::{IntT, Type, compound::StructT, derivations::*},
|
|
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};
|
|
|
|
// Maybe unsafe but honestly it's extremely unlikely that this will go wrong and it won't cause any issues.
|
|
struct Id(UnsafeCell<u32>);
|
|
unsafe impl Send for Id {}
|
|
unsafe impl Sync for Id {}
|
|
|
|
#[derive(Debug, Display)]
|
|
#[debug("{variant:?}")]
|
|
#[display("%{}", unsafe { *id.0.get() })]
|
|
pub struct Instruction<'l> {
|
|
id: Id,
|
|
pub parent_block: &'l Block<'l>,
|
|
pub variant: InstructionVariant<'l>,
|
|
}
|
|
|
|
impl Eq for Instruction<'_> {}
|
|
|
|
impl PartialEq for Instruction<'_> {
|
|
#[inline]
|
|
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) {
|
|
std::ptr::hash(self, state);
|
|
}
|
|
}
|
|
|
|
impl<'l> Deref for Instruction<'l> {
|
|
type Target = InstructionVariant<'l>;
|
|
|
|
#[inline]
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.variant
|
|
}
|
|
}
|
|
|
|
impl<'l> Instruction<'l> {
|
|
#[inline]
|
|
pub fn ctx(&self) -> Ctx<'l> {
|
|
self.parent_block.func.declaring_assembly.ctx()
|
|
}
|
|
|
|
#[inline]
|
|
pub fn id(&self) -> u32 {
|
|
unsafe { *self.id.0.get() }
|
|
}
|
|
}
|
|
|
|
impl<'l> Value<'l> for &'l Instruction<'l> {
|
|
fn ty(&self) -> Type<'l> {
|
|
match self.variant {
|
|
InstructionVariant::Return(_) => Type::Void,
|
|
InstructionVariant::Store(_, _) => Type::Void,
|
|
InstructionVariant::StackAlloc(ty) => ty.make_ptr(true).into(),
|
|
InstructionVariant::Load(value) => match value.ty() {
|
|
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::GetElementVal(v, i) => match v.ty() {
|
|
Type::Struct(StructT { fields, .. }) => {
|
|
let AnyValue::Constant(AnyConst::Str(name)) = i else {
|
|
unreachable!()
|
|
};
|
|
fields.get().unwrap()[name].ty
|
|
}
|
|
_ => 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::IntToPtr(_, t) => Type::Ptr(t),
|
|
InstructionVariant::PtrToInt(_, 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::MakeStruct(t, _) => Type::Struct(t),
|
|
InstructionVariant::Call(f, _) => f.ty.ret_t,
|
|
InstructionVariant::Jump(_) => Type::Void,
|
|
InstructionVariant::Branch { .. } => Type::Void,
|
|
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(),
|
|
}
|
|
}
|
|
|
|
fn get_associated_value(&self, name: &str) -> Option<AnyValue<'l>> {
|
|
default_associated_values(self, name)
|
|
}
|
|
|
|
fn as_any_value(&self) -> AnyValue<'l> {
|
|
AnyValue::Instruction(self)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub enum Cmp {
|
|
Eq,
|
|
Ne,
|
|
Lt,
|
|
Gt,
|
|
Le,
|
|
Ge,
|
|
}
|
|
|
|
#[derive(PartialEq, Eq)]
|
|
pub enum InstructionVariant<'l> {
|
|
StackAlloc(Type<'l>),
|
|
GCAlloc(Type<'l>),
|
|
|
|
Load(AnyValue<'l>),
|
|
Store(AnyValue<'l>, AnyValue<'l>),
|
|
GetElementVal(AnyValue<'l>, AnyValue<'l>),
|
|
GetElementPtr(AnyValue<'l>, AnyValue<'l>),
|
|
|
|
IAdd(AnyValue<'l>, AnyValue<'l>),
|
|
ISub(AnyValue<'l>, AnyValue<'l>),
|
|
IMul(AnyValue<'l>, AnyValue<'l>),
|
|
IDiv(AnyValue<'l>, AnyValue<'l>),
|
|
IMod(AnyValue<'l>, AnyValue<'l>),
|
|
SExt(AnyValue<'l>, IntT),
|
|
ZExt(AnyValue<'l>, IntT),
|
|
Trunc(AnyValue<'l>, IntT),
|
|
IntToPtr(AnyValue<'l>, &'l PtrT<'l>),
|
|
PtrToInt(AnyValue<'l>, IntT),
|
|
FAdd(AnyValue<'l>, AnyValue<'l>),
|
|
FSub(AnyValue<'l>, AnyValue<'l>),
|
|
FMul(AnyValue<'l>, AnyValue<'l>),
|
|
FDiv(AnyValue<'l>, AnyValue<'l>),
|
|
FMod(AnyValue<'l>, AnyValue<'l>),
|
|
|
|
ICmp(AnyValue<'l>, AnyValue<'l>, Cmp),
|
|
FCmp(AnyValue<'l>, AnyValue<'l>, Cmp),
|
|
|
|
MakeStruct(&'l StructT<'l>, &'l [AnyValue<'l>]),
|
|
|
|
Call(&'l Function<'l>, Vec<AnyValue<'l>>),
|
|
Jump(&'l Block<'l>),
|
|
Branch {
|
|
cond: AnyValue<'l>,
|
|
true_case: &'l Block<'l>,
|
|
false_case: &'l Block<'l>,
|
|
},
|
|
Return(Option<AnyValue<'l>>),
|
|
|
|
Reinterpret(AnyValue<'l>, Type<'l>, ValueFlags),
|
|
}
|
|
|
|
impl InstructionVariant<'_> {
|
|
pub fn is_block_termination(&self) -> bool {
|
|
matches!(self, Self::Return(_) | Self::Jump(_) | Self::Branch { .. })
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Debug for InstructionVariant<'_> {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Self::StackAlloc(ty) => write!(f, "stackalloc {ty}"),
|
|
Self::GCAlloc(ty) => write!(f, "gcalloc {ty}"),
|
|
Self::Load(v) => write!(f, "load {v}"),
|
|
Self::Store(t, v) => write!(f, "store {t}, {v}"),
|
|
Self::GetElementVal(v, i) => write!(f, "gev {v}, {i}"),
|
|
Self::GetElementPtr(v, i) => write!(f, "gep {v}, {i}"),
|
|
Self::IntToPtr(v, t) => write!(f, "itp {v}, {t}"),
|
|
Self::PtrToInt(v, t) => write!(f, "pti {v}, {t}"),
|
|
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::MakeStruct(ty, vals) => {
|
|
write!(
|
|
f,
|
|
"struct {} {{",
|
|
match ty.name {
|
|
"" => "<anonymous struct>",
|
|
_ => ty.name,
|
|
}
|
|
)?;
|
|
let mut separator = "";
|
|
for val in *vals {
|
|
write!(f, "{separator}{val}")?;
|
|
separator = ", ";
|
|
}
|
|
write!(f, "}}")
|
|
}
|
|
|
|
Self::Call(func, args) => {
|
|
write!(
|
|
f,
|
|
"call {}(",
|
|
match func.name {
|
|
"" => "<anonymous function>",
|
|
_ => func.name,
|
|
}
|
|
)?;
|
|
let mut separator = "";
|
|
for arg in args {
|
|
write!(f, "{separator}{arg}")?;
|
|
separator = ", ";
|
|
}
|
|
write!(f, ")")
|
|
}
|
|
Self::Branch {
|
|
cond,
|
|
true_case,
|
|
false_case,
|
|
} => write!(f, "br {cond} #{}, #{}", true_case.id, false_case.id),
|
|
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:?}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct Block<'l> {
|
|
pub id: u32,
|
|
pub 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,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Eq for Block<'_> {}
|
|
|
|
impl PartialEq for Block<'_> {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
std::ptr::eq(self, other)
|
|
}
|
|
}
|
|
|
|
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, AnyValue<'l>> {
|
|
let inst = self.push_instruction(InstructionVariant::StackAlloc(ty))?;
|
|
Ok(inst.into())
|
|
}
|
|
|
|
pub fn store(
|
|
&mut self,
|
|
target: AnyValue<'l>,
|
|
value: AnyValue<'l>,
|
|
) -> BlockBuilderResult<'l, AnyValue<'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: AnyValue<'l>) -> BlockBuilderResult<'l, AnyValue<'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 add(
|
|
&mut self,
|
|
a: AnyValue<'l>,
|
|
b: AnyValue<'l>,
|
|
) -> BlockBuilderResult<'l, AnyValue<'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::IAdd(a, b))?;
|
|
Ok(inst.into())
|
|
}
|
|
(Type::Float(a_ty), Type::Float(b_ty)) if a_ty == b_ty => {
|
|
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()),
|
|
}
|
|
}
|
|
|
|
pub fn sub(
|
|
&mut self,
|
|
a: AnyValue<'l>,
|
|
b: AnyValue<'l>,
|
|
) -> BlockBuilderResult<'l, AnyValue<'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()),
|
|
}
|
|
}
|
|
|
|
pub fn mul(
|
|
&mut self,
|
|
a: AnyValue<'l>,
|
|
b: AnyValue<'l>,
|
|
) -> BlockBuilderResult<'l, AnyValue<'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::IMul(a, b))?;
|
|
Ok(inst.into())
|
|
}
|
|
(Type::Float(a_ty), Type::Float(b_ty)) if a_ty == b_ty => {
|
|
let inst = self.push_instruction(InstructionVariant::FMul(a, b))?;
|
|
Ok(inst.into())
|
|
}
|
|
_ => Err(format!("Cannot multiply values of type `{a_ty}` and `{b_ty}`.").into()),
|
|
}
|
|
}
|
|
|
|
pub fn div(
|
|
&mut self,
|
|
a: AnyValue<'l>,
|
|
b: AnyValue<'l>,
|
|
) -> BlockBuilderResult<'l, AnyValue<'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: AnyValue<'l>,
|
|
b: AnyValue<'l>,
|
|
) -> BlockBuilderResult<'l, AnyValue<'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: AnyValue<'l>,
|
|
target: IntT,
|
|
) -> BlockBuilderResult<'l, AnyValue<'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(),
|
|
),
|
|
}
|
|
}
|
|
|
|
pub fn int_to_ptr(
|
|
&mut self,
|
|
val: AnyValue<'l>,
|
|
target: &'l PtrT<'l>,
|
|
) -> BlockBuilderResult<'l, AnyValue<'l>> {
|
|
let ty = val.ty();
|
|
match ty {
|
|
Type::Int(_) => {
|
|
let inst = self.push_instruction(InstructionVariant::IntToPtr(val, target))?;
|
|
Ok(inst.into())
|
|
}
|
|
_ => Err(
|
|
format!("Cannot convert value of type `{ty}` to pointer type `{target}`.").into(),
|
|
),
|
|
}
|
|
}
|
|
|
|
pub fn cmp(
|
|
&mut self,
|
|
a: AnyValue<'l>,
|
|
b: AnyValue<'l>,
|
|
cmp: Cmp,
|
|
) -> BlockBuilderResult<'l, AnyValue<'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::ICmp(a, b, cmp))?;
|
|
Ok(inst.into())
|
|
}
|
|
(Type::Float(a_ty), Type::Float(b_ty)) if a_ty == b_ty => {
|
|
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()),
|
|
}
|
|
}
|
|
|
|
pub fn get_element_ptr(
|
|
&mut self,
|
|
value: AnyValue<'l>,
|
|
index: AnyValue<'l>,
|
|
) -> BlockBuilderResult<'l, AnyValue<'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 get_element_value(
|
|
&mut self,
|
|
value: AnyValue<'l>,
|
|
index: AnyValue<'l>,
|
|
) -> BlockBuilderResult<'l, AnyValue<'l>> {
|
|
let v_ty = value.ty();
|
|
|
|
match v_ty {
|
|
Type::Struct(StructT { fields, .. }) => 'val: {
|
|
let AnyValue::Constant(AnyConst::Str(field)) = index else {
|
|
return Err(
|
|
format!("Expeted index type `const str`, found {}.", index.ty()).into(),
|
|
);
|
|
};
|
|
if let Some(fields) = fields.get() {
|
|
if fields.contains_key(field) {
|
|
let inst =
|
|
self.push_instruction(InstructionVariant::GetElementVal(value, index))?;
|
|
break 'val Ok(inst.into());
|
|
}
|
|
}
|
|
Err(format!("Struct does not contain field `{field}`.").into())
|
|
}
|
|
_ => Err(format!("Cannot index a value of type `{}`.", v_ty).into()),
|
|
}
|
|
}
|
|
|
|
pub fn make_struct(
|
|
&mut self,
|
|
struct_ty: &'l StructT<'l>,
|
|
values: &'l [AnyValue<'l>],
|
|
) -> BlockBuilderResult<'l, AnyValue<'l>> {
|
|
let fields = struct_ty.fields.get().unwrap();
|
|
if fields.len() != values.len() {
|
|
return Err(
|
|
format!("Expected {} values, found {}.", fields.len(), values.len()).into(),
|
|
);
|
|
}
|
|
if let Some((i, (a, b))) = fields
|
|
.values()
|
|
.zip(values)
|
|
.enumerate()
|
|
.find(|(_, (a, b))| a.ty != b.ty())
|
|
{
|
|
return Err(format!(
|
|
"Invalid valua at position {i}. Expected type `{}`, found `{}`.",
|
|
a.ty,
|
|
b.ty()
|
|
)
|
|
.into());
|
|
}
|
|
let inst = self.push_instruction(InstructionVariant::MakeStruct(struct_ty, values))?;
|
|
return 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());
|
|
}
|
|
let inst = self.push_instruction(InstructionVariant::Jump(block))?;
|
|
Ok(inst.into())
|
|
}
|
|
|
|
pub fn branch(
|
|
&mut self,
|
|
cond: AnyValue<'l>,
|
|
true_case: &'l Block<'l>,
|
|
false_case: &'l Block<'l>,
|
|
) -> BlockBuilderResult<'l, AnyValue<'l>> {
|
|
if !std::ptr::eq(true_case.func, self.block.func) {
|
|
return Err("Block does not belong to this function.".into());
|
|
}
|
|
if !std::ptr::eq(false_case.func, self.block.func) {
|
|
return Err("Block does not belong to this function.".into());
|
|
}
|
|
if !matches!(cond.ty(), Type::Bool) {
|
|
return Err(format!("Expected value of type `bool`, found `{}`.", cond.ty()).into());
|
|
}
|
|
let inst = self.push_instruction(InstructionVariant::Branch {
|
|
cond,
|
|
true_case,
|
|
false_case,
|
|
})?;
|
|
Ok(inst.into())
|
|
}
|
|
|
|
pub fn call(
|
|
&mut self,
|
|
func: &'l Function<'l>,
|
|
args: Vec<AnyValue<'l>>,
|
|
) -> BlockBuilderResult<'l, AnyValue<'l>> {
|
|
let par_t = &*func.ty.par_t;
|
|
if par_t.len() != args.len() {
|
|
return Err(format!(
|
|
"Invalid parameter count. Expected {}, found {}.",
|
|
par_t.len(),
|
|
args.len()
|
|
)
|
|
.into());
|
|
}
|
|
|
|
if let Some(i) = par_t.iter().zip(&args).position(|(a, b)| *a != b.ty()) {
|
|
return Err(format!(
|
|
"Invalid parameter at position {i}. Expected type `{}`, found `{}`.",
|
|
&par_t[i],
|
|
args[i].ty()
|
|
)
|
|
.into());
|
|
}
|
|
|
|
let inst = self.push_instruction(InstructionVariant::Call(func, args))?;
|
|
Ok(inst.into())
|
|
}
|
|
|
|
pub fn ret(&mut self, value: Option<AnyValue<'l>>) -> BlockBuilderResult<'l, AnyValue<'l>> {
|
|
let ret_t = self.block.func.ty.ret_t;
|
|
let value_ty = match value {
|
|
Some(v) => v.ty(),
|
|
None => Type::Void,
|
|
};
|
|
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())
|
|
}
|
|
|
|
/// 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: AnyValue<'l>,
|
|
ty: Type<'l>,
|
|
flags: ValueFlags,
|
|
) -> BlockBuilderResult<'l, AnyValue<'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() {
|
|
[.., i] => i.is_block_termination(),
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
pub fn build(self) -> BlockBuilderResult<'l, &'l Block<'l>> {
|
|
if !self.has_termination() {
|
|
return Err(format!("Block #{} has no 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: Id(UnsafeCell::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) -> &BlockBuilder<'l> {
|
|
&self.blocks[self.current_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.blocks[self.current_block].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: self.blocks.len() as u32,
|
|
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() {
|
|
unsafe {
|
|
let ptr = inst.id.0.get();
|
|
std::ptr::write(ptr, next_id.next().unwrap());
|
|
}
|
|
}
|
|
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, AnyValue<'l>> {
|
|
self.current_builder().stack_alloc(ty)
|
|
}
|
|
|
|
pub fn store(
|
|
&mut self,
|
|
target: AnyValue<'l>,
|
|
value: AnyValue<'l>,
|
|
) -> BlockBuilderResult<'l, AnyValue<'l>> {
|
|
self.current_builder().store(target, value)
|
|
}
|
|
|
|
pub fn load(&mut self, value: AnyValue<'l>) -> BlockBuilderResult<'l, AnyValue<'l>> {
|
|
self.current_builder().load(value)
|
|
}
|
|
|
|
pub fn add(
|
|
&mut self,
|
|
a: AnyValue<'l>,
|
|
b: AnyValue<'l>,
|
|
) -> BlockBuilderResult<'l, AnyValue<'l>> {
|
|
self.current_builder().add(a, b)
|
|
}
|
|
|
|
pub fn sub(
|
|
&mut self,
|
|
a: AnyValue<'l>,
|
|
b: AnyValue<'l>,
|
|
) -> BlockBuilderResult<'l, AnyValue<'l>> {
|
|
self.current_builder().sub(a, b)
|
|
}
|
|
|
|
pub fn mul(
|
|
&mut self,
|
|
a: AnyValue<'l>,
|
|
b: AnyValue<'l>,
|
|
) -> BlockBuilderResult<'l, AnyValue<'l>> {
|
|
self.current_builder().mul(a, b)
|
|
}
|
|
|
|
pub fn div(
|
|
&mut self,
|
|
a: AnyValue<'l>,
|
|
b: AnyValue<'l>,
|
|
) -> BlockBuilderResult<'l, AnyValue<'l>> {
|
|
self.current_builder().div(a, b)
|
|
}
|
|
|
|
pub fn modulo(
|
|
&mut self,
|
|
a: AnyValue<'l>,
|
|
b: AnyValue<'l>,
|
|
) -> BlockBuilderResult<'l, AnyValue<'l>> {
|
|
self.current_builder().modulo(a, b)
|
|
}
|
|
|
|
pub fn trunc(
|
|
&mut self,
|
|
val: AnyValue<'l>,
|
|
target: IntT,
|
|
) -> BlockBuilderResult<'l, AnyValue<'l>> {
|
|
self.current_builder().trunc(val, target)
|
|
}
|
|
|
|
pub fn int_to_ptr(
|
|
&mut self,
|
|
val: AnyValue<'l>,
|
|
target: &'l PtrT<'l>,
|
|
) -> BlockBuilderResult<'l, AnyValue<'l>> {
|
|
self.current_builder().int_to_ptr(val, target)
|
|
}
|
|
|
|
pub fn cmp(
|
|
&mut self,
|
|
a: AnyValue<'l>,
|
|
b: AnyValue<'l>,
|
|
cmp: Cmp,
|
|
) -> BlockBuilderResult<'l, AnyValue<'l>> {
|
|
self.current_builder().cmp(a, b, cmp)
|
|
}
|
|
|
|
pub fn get_element_ptr(
|
|
&mut self,
|
|
value: AnyValue<'l>,
|
|
index: AnyValue<'l>,
|
|
) -> BlockBuilderResult<'l, AnyValue<'l>> {
|
|
self.current_builder().get_element_ptr(value, index)
|
|
}
|
|
|
|
pub fn get_element_value(
|
|
&mut self,
|
|
value: AnyValue<'l>,
|
|
index: AnyValue<'l>,
|
|
) -> BlockBuilderResult<'l, AnyValue<'l>> {
|
|
self.current_builder().get_element_value(value, index)
|
|
}
|
|
|
|
pub fn make_struct(
|
|
&mut self,
|
|
struct_ty: &'l StructT<'l>,
|
|
values: &'l [AnyValue<'l>],
|
|
) -> BlockBuilderResult<'l, AnyValue<'l>> {
|
|
self.current_builder().make_struct(struct_ty, values)
|
|
}
|
|
|
|
pub fn jump(&mut self, block: &'l Block<'l>) -> BlockBuilderResult<'l, AnyValue<'l>> {
|
|
self.current_builder().jump(block)
|
|
}
|
|
|
|
pub fn branch(
|
|
&mut self,
|
|
cond: AnyValue<'l>,
|
|
true_case: &'l Block<'l>,
|
|
false_case: &'l Block<'l>,
|
|
) -> BlockBuilderResult<'l, AnyValue<'l>> {
|
|
self.current_builder().branch(cond, true_case, false_case)
|
|
}
|
|
|
|
pub fn call(
|
|
&mut self,
|
|
func: &'l Function<'l>,
|
|
args: Vec<AnyValue<'l>>,
|
|
) -> BlockBuilderResult<'l, AnyValue<'l>> {
|
|
self.current_builder().call(func, args)
|
|
}
|
|
|
|
pub fn ret(&mut self, value: Option<AnyValue<'l>>) -> BlockBuilderResult<'l, AnyValue<'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: AnyValue<'l>,
|
|
ty: Type<'l>,
|
|
flags: ValueFlags,
|
|
) -> BlockBuilderResult<'l, AnyValue<'l>> {
|
|
unsafe { self.current_builder().reinterpret(value, ty, flags) }
|
|
}
|
|
|
|
fn current_builder(&mut self) -> &mut BlockBuilder<'l> {
|
|
&mut self.blocks[self.current_block]
|
|
}
|
|
}
|