use std::collections::HashMap;
use std::fmt;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct MirVtable {
pub trait_name: Arc<str>,
pub type_name: Arc<str>,
pub methods: Vec<(Arc<str>, Arc<str>, MirFnSig)>,
}
pub struct MirModule {
pub name: Arc<str>,
pub functions: Vec<MirFunction>,
pub globals: Vec<MirGlobal>,
pub types: Vec<MirTypeDef>,
pub strings: Vec<Arc<str>>,
pub externals: Vec<MirExternal>,
pub vtables: Vec<MirVtable>,
pub trait_methods: HashMap<Arc<str>, Vec<(Arc<str>, MirFnSig)>>,
pub uniforms: Vec<MirUniform>,
}
#[derive(Debug, Clone)]
pub struct MirUniform {
pub name: Arc<str>,
pub ty: MirType,
pub default: Option<MirConst>,
}
impl MirModule {
pub fn new(name: impl Into<Arc<str>>) -> Self {
Self {
name: name.into(),
functions: Vec::new(),
globals: Vec::new(),
types: Vec::new(),
strings: Vec::new(),
externals: Vec::new(),
vtables: Vec::new(),
trait_methods: HashMap::new(),
uniforms: Vec::new(),
}
}
pub fn add_function(&mut self, func: MirFunction) {
self.functions.push(func);
}
pub fn add_global(&mut self, global: MirGlobal) {
self.globals.push(global);
}
pub fn find_global(&self, name: &str) -> Option<&MirGlobal> {
self.globals.iter().find(|g| g.name.as_ref() == name)
}
pub fn add_type(&mut self, ty: MirTypeDef) {
self.types.push(ty);
}
pub fn intern_string(&mut self, s: impl Into<Arc<str>>) -> u32 {
let s = s.into();
if let Some(idx) = self.strings.iter().position(|x| x.as_ref() == s.as_ref()) {
idx as u32
} else {
let idx = self.strings.len() as u32;
self.strings.push(s);
idx
}
}
pub fn find_function(&self, name: &str) -> Option<&MirFunction> {
self.functions.iter().find(|f| f.name.as_ref() == name)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ShaderStage {
Vertex,
Fragment,
Compute,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BindingKind {
UniformBuffer(Arc<str>),
Texture2D,
Sampler,
StorageBuffer(Arc<str>),
}
#[derive(Debug, Clone)]
pub struct ShaderBinding {
pub set: u32,
pub binding: u32,
pub kind: BindingKind,
pub ty: MirType,
}
#[derive(Debug, Clone)]
pub struct MirFunction {
pub name: Arc<str>,
pub sig: MirFnSig,
pub blocks: Option<Vec<MirBlock>>,
pub locals: Vec<MirLocal>,
pub is_public: bool,
pub linkage: Linkage,
pub shader_stage: Option<ShaderStage>,
pub bindings: Vec<ShaderBinding>,
}
impl MirFunction {
pub fn new(name: impl Into<Arc<str>>, sig: MirFnSig) -> Self {
Self {
name: name.into(),
sig,
blocks: None, locals: Vec::new(),
is_public: false,
linkage: Linkage::Internal,
shader_stage: None,
bindings: Vec::new(),
}
}
pub fn declaration(name: impl Into<Arc<str>>, sig: MirFnSig) -> Self {
Self {
name: name.into(),
sig,
blocks: None,
locals: Vec::new(),
is_public: false,
linkage: Linkage::External,
shader_stage: None,
bindings: Vec::new(),
}
}
pub fn is_declaration(&self) -> bool {
self.blocks.is_none()
}
pub fn entry_block(&self) -> Option<&MirBlock> {
self.blocks.as_ref().and_then(|b| b.first())
}
pub fn block(&self, id: BlockId) -> Option<&MirBlock> {
self.blocks.as_ref().and_then(|b| b.get(id.0 as usize))
}
pub fn block_mut(&mut self, id: BlockId) -> Option<&mut MirBlock> {
self.blocks.as_mut().and_then(|b| b.get_mut(id.0 as usize))
}
pub fn add_block(&mut self, block: MirBlock) -> BlockId {
if let Some(blocks) = &mut self.blocks {
let id = BlockId(blocks.len() as u32);
blocks.push(block);
id
} else {
self.blocks = Some(vec![block]);
BlockId(0)
}
}
pub fn add_local(&mut self, local: MirLocal) -> LocalId {
let id = LocalId(self.locals.len() as u32);
self.locals.push(local);
id
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MirFnSig {
pub params: Vec<MirType>,
pub ret: MirType,
pub is_variadic: bool,
pub calling_conv: CallingConv,
}
impl MirFnSig {
pub fn new(params: Vec<MirType>, ret: MirType) -> Self {
Self {
params,
ret,
is_variadic: false,
calling_conv: CallingConv::Quanta,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CallingConv {
Quanta,
C,
Fast,
Cold,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Linkage {
Internal,
External,
Weak,
LinkOnce,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BlockId(pub u32);
impl BlockId {
pub const ENTRY: Self = Self(0);
}
impl fmt::Display for BlockId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "bb{}", self.0)
}
}
#[derive(Debug, Clone)]
pub struct MirBlock {
pub id: BlockId,
pub label: Option<Arc<str>>,
pub stmts: Vec<MirStmt>,
pub terminator: Option<MirTerminator>,
}
impl MirBlock {
pub fn new(id: BlockId) -> Self {
Self {
id,
label: None,
stmts: Vec::new(),
terminator: None,
}
}
pub fn with_label(id: BlockId, label: impl Into<Arc<str>>) -> Self {
Self {
id,
label: Some(label.into()),
stmts: Vec::new(),
terminator: None,
}
}
pub fn push_stmt(&mut self, stmt: MirStmt) {
self.stmts.push(stmt);
}
pub fn set_terminator(&mut self, term: MirTerminator) {
self.terminator = Some(term);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct LocalId(pub u32);
impl fmt::Display for LocalId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "_{}", self.0)
}
}
#[derive(Debug, Clone)]
pub struct MirLocal {
pub id: LocalId,
pub name: Option<Arc<str>>,
pub ty: MirType,
pub is_mut: bool,
pub is_param: bool,
pub annotations: Vec<Arc<str>>,
}
impl MirLocal {
pub fn new(id: LocalId, ty: MirType) -> Self {
Self {
id,
name: None,
ty,
is_mut: false,
is_param: false,
annotations: Vec::new(),
}
}
pub fn named(id: LocalId, name: impl Into<Arc<str>>, ty: MirType) -> Self {
Self {
id,
name: Some(name.into()),
ty,
is_mut: false,
is_param: false,
annotations: Vec::new(),
}
}
}
#[derive(Debug, Clone)]
pub enum MirValue {
Local(LocalId),
Const(MirConst),
Global(Arc<str>),
Function(Arc<str>),
}
impl fmt::Display for MirValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MirValue::Local(id) => write!(f, "{}", id),
MirValue::Const(c) => write!(f, "{}", c),
MirValue::Global(name) => write!(f, "@{}", name),
MirValue::Function(name) => write!(f, "@{}", name),
}
}
}
#[derive(Debug, Clone)]
pub struct MirStmt {
pub kind: MirStmtKind,
}
impl MirStmt {
pub fn new(kind: MirStmtKind) -> Self {
Self { kind }
}
pub fn assign(dest: LocalId, value: MirRValue) -> Self {
Self::new(MirStmtKind::Assign { dest, value })
}
pub fn storage_live(local: LocalId) -> Self {
Self::new(MirStmtKind::StorageLive(local))
}
pub fn storage_dead(local: LocalId) -> Self {
Self::new(MirStmtKind::StorageDead(local))
}
pub fn nop() -> Self {
Self::new(MirStmtKind::Nop)
}
}
#[derive(Debug, Clone)]
pub enum MirStmtKind {
Assign { dest: LocalId, value: MirRValue },
DerefAssign { ptr: LocalId, value: MirRValue },
FieldDerefAssign {
ptr: LocalId,
field_name: Arc<str>,
value: MirRValue,
},
FieldAssign {
base: LocalId,
field_name: Arc<str>,
value: MirRValue,
},
StorageLive(LocalId),
StorageDead(LocalId),
Nop,
}
#[derive(Debug, Clone)]
pub enum MirRValue {
Use(MirValue),
BinaryOp {
op: BinOp,
left: MirValue,
right: MirValue,
},
UnaryOp { op: UnaryOp, operand: MirValue },
Ref { is_mut: bool, place: MirPlace },
AddressOf { is_mut: bool, place: MirPlace },
Cast {
kind: CastKind,
value: MirValue,
ty: MirType,
},
Aggregate {
kind: AggregateKind,
operands: Vec<MirValue>,
},
Repeat { value: MirValue, count: u64 },
Discriminant(MirPlace),
Len(MirPlace),
NullaryOp(NullaryOp, MirType),
FieldAccess {
base: MirValue,
field_name: Arc<str>,
field_ty: MirType,
},
VariantField {
base: MirValue,
variant_name: Arc<str>,
field_index: u32,
field_ty: MirType,
},
IndexAccess {
base: MirValue,
index: MirValue,
elem_ty: MirType,
},
Deref { ptr: MirValue, pointee_ty: MirType },
TextureSample {
texture: MirValue,
sampler: MirValue,
coords: MirValue,
},
}
#[derive(Debug, Clone)]
pub struct MirPlace {
pub local: LocalId,
pub projections: Vec<PlaceProjection>,
}
impl MirPlace {
pub fn local(id: LocalId) -> Self {
Self {
local: id,
projections: Vec::new(),
}
}
pub fn field(mut self, idx: u32, ty: MirType) -> Self {
self.projections.push(PlaceProjection::Field(idx, ty));
self
}
pub fn index(mut self, idx: LocalId) -> Self {
self.projections.push(PlaceProjection::Index(idx));
self
}
pub fn deref(mut self) -> Self {
self.projections.push(PlaceProjection::Deref);
self
}
}
#[derive(Debug, Clone)]
pub enum PlaceProjection {
Deref,
Field(u32, MirType),
Index(LocalId),
ConstantIndex { offset: u64, from_end: bool },
Subslice { from: u64, to: u64, from_end: bool },
Downcast(u32),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BinOp {
Add,
Sub,
Mul,
Div,
Rem,
Pow,
BitAnd,
BitOr,
BitXor,
Shl,
Shr,
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
AddChecked,
SubChecked,
MulChecked,
AddWrapping,
SubWrapping,
MulWrapping,
AddSaturating,
SubSaturating,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UnaryOp {
Not,
Neg,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CastKind {
IntToInt,
FloatToFloat,
IntToFloat,
FloatToInt,
PtrToInt,
IntToPtr,
PtrToPtr,
FnToPtr,
Transmute,
}
#[derive(Debug, Clone)]
pub enum AggregateKind {
Array(MirType),
Tuple,
Struct(Arc<str>),
Variant(Arc<str>, u32, Arc<str>),
Closure(Arc<str>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NullaryOp {
SizeOf,
AlignOf,
}
#[derive(Debug, Clone)]
pub enum MirTerminator {
Goto(BlockId),
If {
cond: MirValue,
then_block: BlockId,
else_block: BlockId,
},
Switch {
value: MirValue,
targets: Vec<(MirConst, BlockId)>,
default: BlockId,
},
Call {
func: MirValue,
args: Vec<MirValue>,
dest: Option<LocalId>,
target: Option<BlockId>,
unwind: Option<BlockId>,
},
Return(Option<MirValue>),
Unreachable,
Drop {
place: MirPlace,
target: BlockId,
unwind: Option<BlockId>,
},
Assert {
cond: MirValue,
expected: bool,
msg: Arc<str>,
target: BlockId,
unwind: Option<BlockId>,
},
Resume,
Abort,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum MirType {
Void,
Bool,
Int(IntSize, bool), Float(FloatSize),
Ptr(Box<MirType>),
Array(Box<MirType>, u64),
Slice(Box<MirType>),
Struct(Arc<str>),
FnPtr(Box<MirFnSig>),
Never,
Vector(Box<MirType>, u32),
Texture2D(Box<MirType>),
Sampler,
SampledImage(Box<MirType>),
TraitObject(Arc<str>),
Vec(Box<MirType>),
Map(Box<MirType>, Box<MirType>),
Tuple(Vec<MirType>),
}
impl MirType {
pub fn i8() -> Self {
MirType::Int(IntSize::I8, true)
}
pub fn i16() -> Self {
MirType::Int(IntSize::I16, true)
}
pub fn i32() -> Self {
MirType::Int(IntSize::I32, true)
}
pub fn i64() -> Self {
MirType::Int(IntSize::I64, true)
}
pub fn u8() -> Self {
MirType::Int(IntSize::I8, false)
}
pub fn u16() -> Self {
MirType::Int(IntSize::I16, false)
}
pub fn u32() -> Self {
MirType::Int(IntSize::I32, false)
}
pub fn u64() -> Self {
MirType::Int(IntSize::I64, false)
}
pub fn isize() -> Self {
MirType::Int(IntSize::ISize, true)
}
pub fn usize() -> Self {
MirType::Int(IntSize::ISize, false)
}
pub fn f32() -> Self {
MirType::Float(FloatSize::F32)
}
pub fn f64() -> Self {
MirType::Float(FloatSize::F64)
}
pub fn vector(elem: MirType, lanes: u32) -> Self {
MirType::Vector(Box::new(elem), lanes)
}
pub fn v4f32() -> Self {
MirType::vector(MirType::f32(), 4)
}
pub fn v8f32() -> Self {
MirType::vector(MirType::f32(), 8)
}
pub fn v2f64() -> Self {
MirType::vector(MirType::f64(), 2)
}
pub fn v4f64() -> Self {
MirType::vector(MirType::f64(), 4)
}
pub fn v4i32() -> Self {
MirType::vector(MirType::i32(), 4)
}
pub fn v8i32() -> Self {
MirType::vector(MirType::i32(), 8)
}
pub fn v16i8() -> Self {
MirType::vector(MirType::i8(), 16)
}
pub fn v32i8() -> Self {
MirType::vector(MirType::i8(), 32)
}
pub fn texture2d(elem: MirType) -> Self {
MirType::Texture2D(Box::new(elem))
}
pub fn sampler() -> Self {
MirType::Sampler
}
pub fn sampled_image(elem: MirType) -> Self {
MirType::SampledImage(Box::new(elem))
}
pub fn tuple(elems: Vec<MirType>) -> Self {
MirType::Tuple(elems)
}
pub fn tuple_type_name(elems: &[MirType]) -> Arc<str> {
let parts: Vec<&str> = elems
.iter()
.map(|t| match t {
MirType::Void => "void",
MirType::Bool => "bool",
MirType::Int(IntSize::I8, true) => "i8",
MirType::Int(IntSize::I16, true) => "i16",
MirType::Int(IntSize::I32, true) => "i32",
MirType::Int(IntSize::I64, true) => "i64",
MirType::Int(IntSize::I128, true) => "i128",
MirType::Int(IntSize::ISize, true) => "isize",
MirType::Int(IntSize::I8, false) => "u8",
MirType::Int(IntSize::I16, false) => "u16",
MirType::Int(IntSize::I32, false) => "u32",
MirType::Int(IntSize::I64, false) => "u64",
MirType::Int(IntSize::I128, false) => "u128",
MirType::Int(IntSize::ISize, false) => "usize",
MirType::Float(FloatSize::F32) => "f32",
MirType::Float(FloatSize::F64) => "f64",
MirType::Ptr(_) => "ptr",
MirType::Struct(name) => name.as_ref(),
_ => "unknown",
})
.collect();
Arc::from(format!("Tuple_{}", parts.join("_")))
}
pub fn is_signed(&self) -> bool {
matches!(self, MirType::Int(_, true))
}
pub fn is_integer(&self) -> bool {
matches!(self, MirType::Int(_, _))
}
pub fn is_float(&self) -> bool {
matches!(self, MirType::Float(_))
}
pub fn is_pointer(&self) -> bool {
matches!(self, MirType::Ptr(_))
}
pub fn bit_size(&self, ptr_size: u32) -> Option<u32> {
match self {
MirType::Void => Some(0),
MirType::Bool => Some(8),
MirType::Int(size, _) => Some(size.bits(ptr_size)),
MirType::Float(size) => Some(size.bits()),
MirType::Ptr(_) => Some(ptr_size),
MirType::Array(elem, count) => elem.bit_size(ptr_size).map(|s| s * (*count as u32)),
MirType::Slice(_) => Some(ptr_size * 2), MirType::Struct(_) => None, MirType::FnPtr(_) => Some(ptr_size),
MirType::Never => Some(0),
MirType::Vector(elem, lanes) => elem.bit_size(ptr_size).map(|s| s * lanes),
MirType::Texture2D(_) | MirType::Sampler | MirType::SampledImage(_) => {
None }
MirType::TraitObject(_) => Some(ptr_size * 2), MirType::Vec(_) => Some(ptr_size), MirType::Map(_, _) => Some(ptr_size), MirType::Tuple(elems) => {
let mut total = 0u32;
for e in elems {
total += e.bit_size(ptr_size)?;
}
Some(total)
}
}
}
}
impl fmt::Display for MirType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MirType::Void => write!(f, "void"),
MirType::Bool => write!(f, "bool"),
MirType::Int(size, signed) => {
let prefix = if *signed { "i" } else { "u" };
match size {
IntSize::I8 => write!(f, "{}8", prefix),
IntSize::I16 => write!(f, "{}16", prefix),
IntSize::I32 => write!(f, "{}32", prefix),
IntSize::I64 => write!(f, "{}64", prefix),
IntSize::I128 => write!(f, "{}128", prefix),
IntSize::ISize => write!(f, "{}size", prefix),
}
}
MirType::Float(size) => match size {
FloatSize::F32 => write!(f, "f32"),
FloatSize::F64 => write!(f, "f64"),
},
MirType::Ptr(inner) => write!(f, "*{}", inner),
MirType::Array(elem, len) => write!(f, "[{}; {}]", elem, len),
MirType::Slice(elem) => write!(f, "[{}]", elem),
MirType::Struct(name) => write!(f, "{}", name),
MirType::FnPtr(sig) => {
write!(f, "fn(")?;
for (i, p) in sig.params.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", p)?;
}
write!(f, ") -> {}", sig.ret)
}
MirType::Never => write!(f, "!"),
MirType::Vector(elem, lanes) => write!(f, "<{} x {}>", lanes, elem),
MirType::Texture2D(elem) => write!(f, "texture2d<{}>", elem),
MirType::Sampler => write!(f, "sampler"),
MirType::SampledImage(elem) => write!(f, "sampled_image<{}>", elem),
MirType::TraitObject(name) => write!(f, "dyn {}", name),
MirType::Vec(elem) => write!(f, "Vec<{}>", elem),
MirType::Map(key, val) => write!(f, "HashMap<{}, {}>", key, val),
MirType::Tuple(elems) => {
write!(f, "(")?;
for (i, e) in elems.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", e)?;
}
write!(f, ")")
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum IntSize {
I8,
I16,
I32,
I64,
I128,
ISize,
}
impl IntSize {
pub fn bits(&self, ptr_size: u32) -> u32 {
match self {
IntSize::I8 => 8,
IntSize::I16 => 16,
IntSize::I32 => 32,
IntSize::I64 => 64,
IntSize::I128 => 128,
IntSize::ISize => ptr_size,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FloatSize {
F32,
F64,
}
impl FloatSize {
pub fn bits(&self) -> u32 {
match self {
FloatSize::F32 => 32,
FloatSize::F64 => 64,
}
}
}
#[derive(Debug, Clone)]
pub enum MirConst {
Bool(bool),
Int(i128, MirType),
Uint(u128, MirType),
Float(f64, MirType),
Str(u32),
ByteStr(Vec<u8>),
Null(MirType),
Unit,
Zeroed(MirType),
Undef(MirType),
Struct(Arc<str>, Vec<MirConst>),
}
impl fmt::Display for MirConst {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MirConst::Bool(b) => write!(f, "{}", b),
MirConst::Int(v, ty) => write!(f, "{}{}", v, ty),
MirConst::Uint(v, ty) => write!(f, "{}{}", v, ty),
MirConst::Float(v, ty) => write!(f, "{}{}", v, ty),
MirConst::Str(idx) => write!(f, "str#{}", idx),
MirConst::ByteStr(bytes) => write!(f, "b\"{}\"", bytes.len()),
MirConst::Null(ty) => write!(f, "null:{}", ty),
MirConst::Unit => write!(f, "()"),
MirConst::Zeroed(ty) => write!(f, "zeroed:{}", ty),
MirConst::Undef(ty) => write!(f, "undef:{}", ty),
MirConst::Struct(name, fields) => {
write!(f, "{}{{", name)?;
for (i, fv) in fields.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", fv)?;
}
write!(f, "}}")
}
}
}
}
#[derive(Debug, Clone)]
pub struct MirGlobal {
pub name: Arc<str>,
pub ty: MirType,
pub init: Option<MirConst>,
pub is_mut: bool,
pub linkage: Linkage,
}
impl MirGlobal {
pub fn new(name: impl Into<Arc<str>>, ty: MirType) -> Self {
Self {
name: name.into(),
ty,
init: None,
is_mut: false,
linkage: Linkage::Internal,
}
}
}
#[derive(Debug, Clone)]
pub struct MirExternal {
pub name: Arc<str>,
pub kind: ExternalKind,
}
#[derive(Debug, Clone)]
pub enum ExternalKind {
Function(MirFnSig),
Global(MirType),
}
#[derive(Debug, Clone)]
pub struct MirTypeDef {
pub name: Arc<str>,
pub kind: TypeDefKind,
}
#[derive(Debug, Clone)]
pub enum TypeDefKind {
Struct {
fields: Vec<(Option<Arc<str>>, MirType)>,
packed: bool,
},
Union { variants: Vec<(Arc<str>, MirType)> },
Enum {
discriminant_ty: MirType,
variants: Vec<MirEnumVariant>,
},
}
#[derive(Debug, Clone)]
pub struct MirEnumVariant {
pub name: Arc<str>,
pub discriminant: i128,
pub fields: Vec<(Option<Arc<str>>, MirType)>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mir_type_display() {
assert_eq!(format!("{}", MirType::i32()), "i32");
assert_eq!(format!("{}", MirType::u64()), "u64");
assert_eq!(format!("{}", MirType::f32()), "f32");
assert_eq!(format!("{}", MirType::Bool), "bool");
assert_eq!(
format!("{}", MirType::Ptr(Box::new(MirType::i32()))),
"*i32"
);
assert_eq!(format!("{}", MirType::Void), "void");
assert_eq!(format!("{}", MirType::Never), "!");
}
#[test]
fn test_mir_type_constructors() {
assert_eq!(MirType::i8(), MirType::Int(IntSize::I8, true));
assert_eq!(MirType::i16(), MirType::Int(IntSize::I16, true));
assert_eq!(MirType::i32(), MirType::Int(IntSize::I32, true));
assert_eq!(MirType::i64(), MirType::Int(IntSize::I64, true));
assert_eq!(MirType::u8(), MirType::Int(IntSize::I8, false));
assert_eq!(MirType::u16(), MirType::Int(IntSize::I16, false));
assert_eq!(MirType::u32(), MirType::Int(IntSize::I32, false));
assert_eq!(MirType::u64(), MirType::Int(IntSize::I64, false));
assert_eq!(MirType::isize(), MirType::Int(IntSize::ISize, true));
assert_eq!(MirType::usize(), MirType::Int(IntSize::ISize, false));
assert_eq!(MirType::f32(), MirType::Float(FloatSize::F32));
assert_eq!(MirType::f64(), MirType::Float(FloatSize::F64));
}
#[test]
fn test_mir_type_predicates() {
assert!(MirType::i32().is_integer());
assert!(MirType::u64().is_integer());
assert!(!MirType::f32().is_integer());
assert!(!MirType::Bool.is_integer());
assert!(MirType::f32().is_float());
assert!(MirType::f64().is_float());
assert!(!MirType::i32().is_float());
assert!(MirType::i32().is_signed());
assert!(!MirType::u32().is_signed());
assert!(MirType::Ptr(Box::new(MirType::i32())).is_pointer());
assert!(!MirType::i32().is_pointer());
}
#[test]
fn test_mir_type_bit_size() {
let ptr_size = 64;
assert_eq!(MirType::Void.bit_size(ptr_size), Some(0));
assert_eq!(MirType::Bool.bit_size(ptr_size), Some(8));
assert_eq!(MirType::i8().bit_size(ptr_size), Some(8));
assert_eq!(MirType::i16().bit_size(ptr_size), Some(16));
assert_eq!(MirType::i32().bit_size(ptr_size), Some(32));
assert_eq!(MirType::i64().bit_size(ptr_size), Some(64));
assert_eq!(
MirType::Int(IntSize::I128, true).bit_size(ptr_size),
Some(128)
);
assert_eq!(MirType::f32().bit_size(ptr_size), Some(32));
assert_eq!(MirType::f64().bit_size(ptr_size), Some(64));
assert_eq!(
MirType::Ptr(Box::new(MirType::i32())).bit_size(ptr_size),
Some(64)
);
assert_eq!(MirType::isize().bit_size(ptr_size), Some(64));
}
#[test]
fn test_int_size_bits() {
assert_eq!(IntSize::I8.bits(64), 8);
assert_eq!(IntSize::I16.bits(64), 16);
assert_eq!(IntSize::I32.bits(64), 32);
assert_eq!(IntSize::I64.bits(64), 64);
assert_eq!(IntSize::I128.bits(64), 128);
assert_eq!(IntSize::ISize.bits(64), 64);
assert_eq!(IntSize::ISize.bits(32), 32);
}
#[test]
fn test_float_size_bits() {
assert_eq!(FloatSize::F32.bits(), 32);
assert_eq!(FloatSize::F64.bits(), 64);
}
#[test]
fn test_mir_module() {
let mut module = MirModule::new("test");
let idx = module.intern_string("hello");
assert_eq!(idx, 0);
assert_eq!(module.intern_string("hello"), 0); assert_eq!(module.intern_string("world"), 1); }
#[test]
fn test_mir_module_add_function() {
let mut module = MirModule::new("test");
let sig = MirFnSig::new(vec![MirType::i32()], MirType::i32());
let func = MirFunction::new("my_func", sig);
module.add_function(func);
assert_eq!(module.functions.len(), 1);
assert_eq!(module.functions[0].name.as_ref(), "my_func");
}
#[test]
fn test_mir_module_add_global() {
let mut module = MirModule::new("test");
let global = MirGlobal::new("my_global", MirType::i32());
module.add_global(global);
assert_eq!(module.globals.len(), 1);
assert_eq!(module.globals[0].name.as_ref(), "my_global");
}
#[test]
fn test_mir_function_new() {
let sig = MirFnSig::new(vec![MirType::i32(), MirType::i64()], MirType::Bool);
let func = MirFunction::new("test_func", sig);
assert_eq!(func.name.as_ref(), "test_func");
assert_eq!(func.sig.params.len(), 2);
assert_eq!(func.sig.ret, MirType::Bool);
assert!(func.blocks.is_none());
}
#[test]
fn test_mir_function_is_declaration() {
let sig = MirFnSig::new(vec![], MirType::Void);
let mut func = MirFunction::new("decl_func", sig);
assert!(func.is_declaration());
func.add_block(MirBlock::new(BlockId::ENTRY));
assert!(!func.is_declaration());
}
#[test]
fn test_mir_function_add_block() {
let sig = MirFnSig::new(vec![], MirType::Void);
let mut func = MirFunction::new("test", sig);
func.add_block(MirBlock::new(BlockId(0)));
func.add_block(MirBlock::new(BlockId(1)));
assert_eq!(func.blocks.as_ref().unwrap().len(), 2);
}
#[test]
fn test_mir_block_new() {
let block = MirBlock::new(BlockId(5));
assert_eq!(block.id.0, 5);
assert!(block.label.is_none());
assert!(block.stmts.is_empty());
assert!(block.terminator.is_none());
}
#[test]
fn test_mir_block_with_label() {
let block = MirBlock::with_label(BlockId(0), "entry");
assert_eq!(block.label.as_ref().unwrap().as_ref(), "entry");
}
#[test]
fn test_mir_block_push_stmt() {
let mut block = MirBlock::new(BlockId(0));
block.push_stmt(MirStmt::nop());
block.push_stmt(MirStmt::nop());
assert_eq!(block.stmts.len(), 2);
}
#[test]
fn test_mir_block_set_terminator() {
let mut block = MirBlock::new(BlockId(0));
assert!(block.terminator.is_none());
block.set_terminator(MirTerminator::Return(None));
assert!(block.terminator.is_some());
}
#[test]
fn test_mir_stmt_assign() {
let stmt = MirStmt::assign(
LocalId(0),
MirRValue::Use(MirValue::Const(MirConst::Bool(true))),
);
match stmt.kind {
MirStmtKind::Assign { dest, .. } => assert_eq!(dest.0, 0),
_ => panic!("Expected Assign"),
}
}
#[test]
fn test_mir_stmt_nop() {
let stmt = MirStmt::nop();
assert!(matches!(stmt.kind, MirStmtKind::Nop));
}
#[test]
fn test_mir_value_local() {
let val = MirValue::Local(LocalId(42));
match val {
MirValue::Local(id) => assert_eq!(id.0, 42),
_ => panic!("Expected Local"),
}
}
#[test]
fn test_mir_value_const_int() {
let val = MirValue::Const(MirConst::Int(123, MirType::i32()));
match val {
MirValue::Const(MirConst::Int(v, ty)) => {
assert_eq!(v, 123);
assert_eq!(ty, MirType::i32());
}
_ => panic!("Expected Const Int"),
}
}
#[test]
fn test_mir_const_display() {
assert_eq!(format!("{}", MirConst::Bool(true)), "true");
assert_eq!(format!("{}", MirConst::Bool(false)), "false");
assert_eq!(format!("{}", MirConst::Int(42, MirType::i32())), "42i32");
assert_eq!(format!("{}", MirConst::Uint(100, MirType::u64())), "100u64");
assert_eq!(format!("{}", MirConst::Str(0)), "str#0");
assert_eq!(format!("{}", MirConst::Unit), "()");
assert_eq!(
format!(
"{}",
MirConst::Struct(
Arc::from("Color"),
vec![
MirConst::Float(1.0, MirType::f64()),
MirConst::Float(0.5, MirType::f64()),
MirConst::Float(0.0, MirType::f64()),
],
)
),
"Color{1f64, 0.5f64, 0f64}",
);
}
#[test]
fn test_mir_terminator_return() {
let term = MirTerminator::Return(Some(MirValue::Const(MirConst::Int(0, MirType::i32()))));
match term {
MirTerminator::Return(Some(_)) => {}
_ => panic!("Expected Return with value"),
}
}
#[test]
fn test_mir_terminator_goto() {
let term = MirTerminator::Goto(BlockId(5));
match term {
MirTerminator::Goto(id) => assert_eq!(id.0, 5),
_ => panic!("Expected Goto"),
}
}
#[test]
fn test_mir_terminator_if() {
let term = MirTerminator::If {
cond: MirValue::Const(MirConst::Bool(true)),
then_block: BlockId(1),
else_block: BlockId(2),
};
match term {
MirTerminator::If {
then_block,
else_block,
..
} => {
assert_eq!(then_block.0, 1);
assert_eq!(else_block.0, 2);
}
_ => panic!("Expected If"),
}
}
#[test]
fn test_mir_rvalue_binary_op() {
let rvalue = MirRValue::BinaryOp {
op: BinOp::Add,
left: MirValue::Local(LocalId(0)),
right: MirValue::Local(LocalId(1)),
};
match rvalue {
MirRValue::BinaryOp { op, .. } => assert_eq!(op, BinOp::Add),
_ => panic!("Expected BinaryOp"),
}
}
#[test]
fn test_mir_rvalue_unary_op() {
let rvalue = MirRValue::UnaryOp {
op: UnaryOp::Neg,
operand: MirValue::Local(LocalId(0)),
};
match rvalue {
MirRValue::UnaryOp { op, .. } => assert_eq!(op, UnaryOp::Neg),
_ => panic!("Expected UnaryOp"),
}
}
#[test]
fn test_mir_global_new() {
let global = MirGlobal::new("my_var", MirType::i64());
assert_eq!(global.name.as_ref(), "my_var");
assert_eq!(global.ty, MirType::i64());
assert!(!global.is_mut);
assert!(global.init.is_none());
}
#[test]
fn test_mir_fn_sig_new() {
let sig = MirFnSig::new(vec![MirType::i32(), MirType::f64()], MirType::Bool);
assert_eq!(sig.params.len(), 2);
assert_eq!(sig.params[0], MirType::i32());
assert_eq!(sig.params[1], MirType::f64());
assert_eq!(sig.ret, MirType::Bool);
assert!(!sig.is_variadic);
}
#[test]
fn test_mir_fn_sig_void() {
let sig = MirFnSig::new(vec![], MirType::Void);
assert!(sig.params.is_empty());
assert_eq!(sig.ret, MirType::Void);
}
#[test]
fn test_block_id_entry() {
assert_eq!(BlockId::ENTRY.0, 0);
}
#[test]
fn test_local_id() {
let id = LocalId(10);
assert_eq!(id.0, 10);
}
}