#![allow(dead_code)] use std::{ cmp::Ordering, collections::{BTreeMap, BTreeSet, HashMap}, }; use crate::{ ast::{IntegralType, Node as AstNode, Tag, Type}, parser::Tree, string_table::{ImmOrIndex, Index as StringsIndex, StringTable}, variant, write_indented, writeln_indented, }; type Node = u32; #[derive(Debug)] enum NodeOrList { Node(Node), // node of alloca location List(Vec), // list of references to `Node(_)` } #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Type2 { Integral(bool, u16), Binary32, Binary64, Bool, Pointer, } impl Into for Type2 { fn into(self) -> mir::Type { self.mir_type() } } impl Type2 { fn mir_type(self) -> mir::Type { match self { Type2::Integral(_, bits) => mir::Type::from_bitsize_int(bits as u32), Type2::Binary32 => mir::Type::SinglePrecision, Type2::Binary64 => mir::Type::DoublePrecision, Type2::Bool => mir::Type::from_bitsize_int(1), Type2::Pointer => mir::Type::QWord, } } fn is_signed(self) -> bool { match self { Type2::Integral(signed, _) => signed, _ => false, } } fn mir_unalignment(self) -> Option<(bool, u16)> { match self { Type2::Integral(signed, bits) => match bits { 8 | 16 | 32 | 64 => None, bits => Some((signed, bits)), }, _ => None, } } fn size(&self) -> u32 { match self { Type2::Integral(_signed, bits) => bits.div_ceil(8) as u32, Type2::Binary32 => 4, Type2::Binary64 => 8, Type2::Bool => 1, Type2::Pointer => 8, } } fn align(&self) -> u32 { self.size() } fn try_from_ast_type(ty: &Type) -> Option { match ty { Type::Bool => Some(Type2::Bool), Type::Integer(i) => Some(Type2::Integral(i.signed, i.bits)), Type::Floating(f) => match f { crate::ast::FloatingType::Binary32 => Some(Type2::Binary32), crate::ast::FloatingType::Binary64 => Some(Type2::Binary64), }, Type::Pointer { .. } => Some(Type2::Pointer), _ => { None //unimplemented!("conversion from {value:?} to triples type not implemented") } } } } impl core::fmt::Display for Type2 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Type2::Integral(signed, bits) => write!(f, "{}{bits}", if *signed { "i" } else { "u" }), Type2::Binary32 => write!(f, "f32"), Type2::Binary64 => write!(f, "f64"), Type2::Bool => write!(f, "bool"), Type2::Pointer => write!(f, "ptr"), } } } impl From for Type2 { fn from(value: Type) -> Self { (&value).into() } } impl From<&Type> for Type2 { fn from(value: &Type) -> Self { match value { Type::Bool => Type2::Bool, Type::Integer(i) => Type2::Integral(i.signed, i.bits), Type::Floating(f) => match f { crate::ast::FloatingType::Binary32 => Type2::Binary32, crate::ast::FloatingType::Binary64 => Type2::Binary64, }, Type::Pointer { .. } => Type2::Pointer, Type::Fn { .. } => Type2::Pointer, _ => { unimplemented!("conversion from {value:?} to triples type not implemented") } } } } #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Inst { /// index Label, /// ast-node -> index ExternRef(Type2), /// index FunctionStart, /// None FunctionEnd, // marker /// lhs Argument(Type2), InlineType(Type2), /// list of arguments /// start, (inlinetype)end Call(Node), /// Value /// lhs GlobalConstant(StringsIndex, Type2), /// u32 ConstantU32, /// lo, hi ConstantU64, /// index ConstantMultiByte, /// size, align Alloca, /// src Load(Type2), /// src, dst Store(Type2), /// ptr, index, GetElementPtr(Type2), /// size, align Parameter(Type2), /// lhs, rhs Add(Type2), /// lhs, rhs Sub(Type2), /// lhs, rhs Mul(Type2), /// lhs, rhs Div(Type2), /// lhs, rhs Rem(Type2), /// lhs, rhs BitAnd(Type2), /// lhs, rhs BitOr(Type2), /// lhs, rhs BitXOr(Type2), /// lhs, rhs ShiftLeft(Type2), /// lhs, rhs ShiftRight(Type2), /// lhs Negate(Type2), /// lhs Not(Type2), /// lhs, rhs Eq(Type2), /// lhs, rhs Neq(Type2), /// lhs, rhs Gt(Type2), /// lhs, rhs Lt(Type2), /// lhs, rhs Ge(Type2), /// lhs, rhs Le(Type2), /// lhs ExplicitCast(Type2, Type2), /// lhs ReturnValue(Type2), /// no parameters Return, /// Node is a bool /// two labels /// lhs, rhs Branch(Node), /// lhs: Label Jump, /// lhs, rhs Phi2(Type2), } impl Inst { fn is_constant(self) -> bool { match self { Inst::ConstantU32 | Inst::ConstantU64 | Inst::ConstantMultiByte => true, _ => false, } } } #[derive(Debug, Clone, Copy)] struct Data { lhs: u32, rhs: u32, } impl Data { fn new(lhs: u32, rhs: u32) -> Self { Self { lhs, rhs } } fn lhs(lhs: u32) -> Data { Self { lhs, rhs: 0 } } fn as_u32(&self) -> u32 { self.lhs } fn as_u64(&self) -> u64 { self.lhs as u64 | (self.rhs as u64) << u32::BITS as u64 } fn as_index(&self) -> StringsIndex { crate::string_table::Index { start: self.lhs, end: self.rhs, } } fn as_lhs_rhs(&self) -> (u32, u32) { (self.lhs, self.rhs) } } impl From for Data { fn from(value: u32) -> Self { Self { lhs: value, rhs: 0 } } } impl From for Data { fn from(value: u64) -> Self { let (lo, hi) = { (value as u32, (value >> u32::BITS as u64) as u32) }; Self { lhs: lo, rhs: hi } } } impl From for Data { fn from(value: crate::string_table::ImmOrIndex) -> Self { match value { ImmOrIndex::U64(v) => v.into(), ImmOrIndex::U32(v) => v.into(), ImmOrIndex::Index(v) => v.into(), } } } impl From for Data { fn from(value: crate::string_table::Index) -> Self { Self { lhs: value.start, rhs: value.end, } } } pub struct IRBuilder<'tree, 'ir> { ir: &'ir mut IR, tree: &'tree mut Tree, type_map: HashMap, lookup: HashMap, fixup: Vec, } impl core::ops::Index for IR { type Output = Inst; fn index(&self, index: Node) -> &Self::Output { &self.nodes[index as usize] } } impl<'tree, 'ir> IRBuilder<'tree, 'ir> { fn new(ir: &'ir mut IR, tree: &'tree mut Tree) -> Self { Self { ir, tree, type_map: HashMap::new(), lookup: HashMap::new(), fixup: vec![], } } fn peer_type_of_nodes(&self, lhs: AstNode, rhs: AstNode) -> Option { let lty = self.tree.type_of_node(lhs); let rty = self.tree.type_of_node(rhs); let peer = lty .equal_type(&rty) .expect(&format!("types {lty} and {rty} are incompatible!")); if lty == Type::ComptimeNumber { eprintln!( "%{lhs} is comptime number: {:?}", self.tree.nodes.get_node(lhs) ); let value = self.tree.value_of_comptime_node(lhs)?; if value.bit_count() > rty.bit_width() { panic!("comptime number is incompatible with type {rty}!"); } } if rty == Type::ComptimeNumber { eprintln!("%{rhs} is comptime number"); let value = self.tree.value_of_comptime_node(rhs)?; if value.bit_count() > lty.bit_width() { panic!("comptime number is incompatible with type {lty}!"); } } Some(peer) } fn visit(&mut self, node: AstNode) -> Node { let tag = &self.tree.nodes[node].clone(); match tag { Tag::FunctionDecl { proto, body } => { variant!( Tag::FunctionProto { name, parameters, .. } = self.tree.nodes.get_node(*proto) ); self.ir.push(Inst::FunctionStart, { variant!(Tag::Ident { name } = self.tree.nodes.get_node(*name)); Some((*name).into()) }); if let Some(parameters) = parameters { variant!( Tag::ParameterList { parameters } = self.tree.nodes.get_node(*parameters) ); for param in parameters { variant!(Tag::Parameter { ty, .. } = self.tree.nodes.get_node(*param)); let ty = self.tree.type_of_node(*ty); let size = ty.size_of(); let align = ty.align_of(); let ir = self .ir .push(Inst::Parameter(ty.into()), Some(Data::new(size, align))); self.lookup.insert(*param, NodeOrList::Node(ir)); } } self.tree.st.into_child(node); let value = self.visit(*body); self.tree.st.into_parent(); if value != !0 { let ty = self.tree.type_of_node(*body); self.ir .push(Inst::ReturnValue(ty.into()), Some(Data::lhs(value))); } self.ir.push(Inst::FunctionEnd, None) } Tag::CallExpr { lhs, rhs } => { let ty = self.tree.type_of_node(*lhs).return_type().unwrap().clone(); let args = if let Some(args) = *rhs { variant!( self.tree.nodes.get_node(args) => Tag::ArgumentList { arguments } ); let args = arguments.clone().iter().map(|arg| { variant!(*self.tree.nodes.get_node(*arg) => Tag::Argument { expr ,..}); (self.visit(expr), self.tree.type_of_node(expr)) }).collect::>(); let start = self.ir.nodes.len(); for (arg, ty) in args { _ = self .ir .push(Inst::Argument(ty.into()), Some(Data::lhs(arg))); } let end = self.ir.nodes.len(); Some((start as u32, end as u32)) } else { None }; let (start, end) = args.unwrap_or((self.ir.nodes.len() as u32, self.ir.nodes.len() as u32)); self.ir.push(Inst::InlineType(ty.into()), None); let func = self.visit(*lhs); self.ir .push(Inst::Call(func), Some(Data::new(start, end + 1))) } Tag::Block { statements, trailing_expr, } => { for stmt in statements { self.visit(*stmt); } if let Some(expr) = trailing_expr { self.visit(*expr) } else { !0 } } Tag::VarDecl { assignment, .. } => { let ty = self.tree.type_of_node(node); let alloca = self .ir .push(Inst::Alloca, Some(Data::new(ty.size_of(), ty.align_of()))); if let Some(assignment) = assignment { let value = self.visit(*assignment); // discard store let _ = self .ir .push(Inst::Store(ty.into()), Some(Data::new(value, alloca))); } self.lookup.insert(node, NodeOrList::Node(alloca)); alloca } Tag::GlobalDecl { name, assignment, .. } => { let ty = self.tree.type_of_node(node); let value = self.visit(*assignment); assert!(self.tree.is_node_comptime(*assignment, true)); variant!(self.tree.nodes.get_node(*name) => Tag::Ident { name }); let global = self.ir.push( Inst::GlobalConstant(*name, ty.into()), Some(Data::lhs(value)), ); self.lookup.insert(node, NodeOrList::Node(global)); global } Tag::GlobalRef(decl) => { let ty = self.tree.type_of_node(*decl); let node = self .ir .push(Inst::ExternRef(ty.into()), Some(Data::lhs(decl.get()))); self.fixup.push(node); node } Tag::DeclRef(decl) => match self.lookup.get_mut(decl) { Some(NodeOrList::Node(decl)) => *decl, lookup => { println!("lookup for ast decl %{}", decl.get()); println!("{lookup:?}"); panic!("should not have any unresolved lookups") } }, Tag::Assign { lhs, rhs } => { let ty = self.tree.type_of_node(node); let dest = self.visit(*lhs); let source = self.visit(*rhs); self.ir .push(Inst::Store(ty.into()), Some(Data::new(source, dest))) } Tag::ReturnStmt { expr } => { if let Some(expr) = expr { let ty = self.tree.type_of_node(*expr); let expr = self.visit(*expr); self.ir .push(Inst::ReturnValue(ty.into()), Some(Data::lhs(expr))) } else { self.ir.push(Inst::Return, None) } } Tag::ExprStmt { expr } => self.visit(*expr), Tag::Deref { lhs } => { let ty = self.tree.type_of_node(*lhs).pointee().unwrap().clone(); let lhs = self.visit(*lhs); self.ir.push(Inst::Load(ty.into()), Some(Data::lhs(lhs))) } Tag::Add { lhs, rhs } => { let ty = self.tree.type_of_node(node); let lhs = self.visit(*lhs); let rhs = self.visit(*rhs); self.ir .push(Inst::Add(ty.into()), Some(Data::new(lhs, rhs))) } Tag::Sub { lhs, rhs } => { let ty = self.tree.type_of_node(node); let lhs = self.visit(*lhs); let rhs = self.visit(*rhs); self.ir .push(Inst::Sub(ty.into()), Some(Data::new(lhs, rhs))) } Tag::BitAnd { lhs, rhs } => { let ty = self.tree.type_of_node(node); let lhs = self.visit(*lhs); let rhs = self.visit(*rhs); self.ir .push(Inst::BitAnd(ty.into()), Some(Data::new(lhs, rhs))) } Tag::BitOr { lhs, rhs } => { let ty = self.tree.type_of_node(node); let lhs = self.visit(*lhs); let rhs = self.visit(*rhs); self.ir .push(Inst::BitOr(ty.into()), Some(Data::new(lhs, rhs))) } Tag::BitXOr { lhs, rhs } => { let ty = self.tree.type_of_node(node); let lhs = self.visit(*lhs); let rhs = self.visit(*rhs); self.ir .push(Inst::BitXOr(ty.into()), Some(Data::new(lhs, rhs))) } Tag::Lt { lhs, rhs } | Tag::Ge { lhs, rhs } | Tag::Le { lhs, rhs } | Tag::Eq { lhs, rhs } | Tag::NEq { lhs, rhs } | Tag::Gt { lhs, rhs } => { let ty = self .tree .peer_type_of_nodes(*lhs, *rhs) .expect({ let at = self.tree.type_of_node(*lhs); let bt = self.tree.type_of_node(*rhs); &format!("incompatible types for %{lhs}({at}) and %{rhs}({bt})") }) .into(); let inst = match tag { Tag::Eq { .. } => Inst::Eq(ty), Tag::NEq { .. } => Inst::Neq(ty), Tag::Gt { .. } => Inst::Gt(ty), Tag::Lt { .. } => Inst::Lt(ty), Tag::Ge { .. } => Inst::Ge(ty), Tag::Le { .. } => Inst::Le(ty), _ => unreachable!(), }; let lhs = self.visit(*lhs); let rhs = self.visit(*rhs); self.ir.push(inst, Some(Data::new(lhs, rhs))) } Tag::Mul { lhs, rhs } => { let ty = self.tree.type_of_node(node); let lhs = self.visit(*lhs); let rhs = self.visit(*rhs); self.ir .push(Inst::Mul(ty.into()), Some(Data::new(lhs, rhs))) } Tag::Negate { lhs } => { let ty = self.tree.type_of_node(node); let lhs = self.visit(*lhs); self.ir.push(Inst::Negate(ty.into()), Some(Data::lhs(lhs))) } Tag::Not { lhs } => { let ty = self.tree.type_of_node(node); let lhs = self.visit(*lhs); self.ir.push(Inst::Not(ty.into()), Some(Data::lhs(lhs))) } Tag::Shl { lhs, rhs } => { let ty = self.tree.type_of_node(node); let lhs = self.visit(*lhs); let rhs = self.visit(*rhs); self.ir .push(Inst::ShiftLeft(ty.into()), Some(Data::new(lhs, rhs))) } Tag::Shr { lhs, rhs } => { let ty = self.tree.type_of_node(node); let lhs = self.visit(*lhs); let rhs = self.visit(*rhs); self.ir .push(Inst::ShiftRight(ty.into()), Some(Data::new(lhs, rhs))) } Tag::Ref { lhs } => { let ty = self.tree.type_of_node(*lhs); let lhs = self.visit(*lhs); // self.ir.push(Inst::Load(ty.into()), Some(Data::lhs(lhs))) // nothing happens here because lhs is of type pointer self.ir .push(Inst::GetElementPtr(ty.into()), Some(Data::new(lhs, 0))) } Tag::Constant { bytes, .. } => match bytes { ImmOrIndex::U64(v) => self.ir.push(Inst::ConstantU64, Some((*v).into())), ImmOrIndex::U32(v) => self.ir.push(Inst::ConstantU32, Some((*v).into())), ImmOrIndex::Index(idx) => { self.ir.push(Inst::ConstantMultiByte, Some((*idx).into())) } }, Tag::ExplicitCast { lhs, typename } => { let l_ty = self.tree.type_of_node(*lhs).clone(); let r_ty = self.tree.type_of_node(*typename).clone(); let lhs = self.visit(*lhs); if l_ty.bit_width() == r_ty.bit_width() { //noop? lhs } else { self.ir.push( Inst::ExplicitCast(l_ty.into(), r_ty.into()), Some(Data::lhs(lhs)), ) } } Tag::IfExpr { condition, body } => { assert_eq!(self.tree.type_of_node(*condition), Type::Bool); let condition = self.visit(*condition); let br = self.ir.push(Inst::Branch(condition), None); let label_lhs = self.ir.push(Inst::Label, Some(StringsIndex::none().into())); let _ = self.visit(*body); let jmp = self.ir.push(Inst::Jump, None); let nojump = self.ir.push(Inst::Label, Some(StringsIndex::none().into())); self.ir.data[br as usize] = Some(Data::new(label_lhs, nojump)); self.ir.data[jmp as usize] = Some(Data::lhs(nojump)); br } Tag::IfElseExpr { condition, body, else_expr, } => { assert_eq!(self.tree.type_of_node(*condition), Type::Bool); let ty = self.tree.peer_type_of_nodes_unwrap(*body, *else_expr); let condition = self.visit(*condition); let br = self.ir.push(Inst::Branch(condition), None); let label_lhs = self.ir.push(Inst::Label, Some(StringsIndex::none().into())); let lhs = self.visit(*body); let ljmp = self.ir.push(Inst::Jump, None); let label_rhs = self.ir.push(Inst::Label, Some(StringsIndex::none().into())); let rhs = self.visit(*else_expr); let rjmp = self.ir.push(Inst::Jump, None); let nojump = self.ir.push(Inst::Label, Some(StringsIndex::none().into())); let phi = if let Some(ty) = Type2::try_from_ast_type(&ty) { self.ir.push(Inst::Phi2(ty), Some(Data::new(lhs, rhs))) } else { br }; self.ir.data[br as usize] = Some(Data::new(label_lhs, label_rhs)); self.ir.data[ljmp as usize] = Some(Data::lhs(nojump)); self.ir.data[rjmp as usize] = Some(Data::lhs(nojump)); phi } _ => { dbg!(&self.tree.nodes[node]); todo!() } } } } pub struct IR { nodes: Vec, data: Vec>, } impl IR { pub fn new() -> Self { Self { nodes: Vec::new(), data: Vec::new(), } } fn push(&mut self, inst: Inst, data: Option) -> u32 { let node = self.nodes.len() as u32; self.nodes.push(inst); self.data.push(data); node } pub fn build<'a, 'tree>(&'a mut self, tree: &'tree mut Tree) -> IRBuilder<'tree, 'a> { let global_decls = tree.global_decls.clone(); let mut builder = IRBuilder::new(self, tree); for node in &global_decls { builder.visit(*node); } for &fix in builder.fixup.iter() { let ast_node = builder.ir.data[fix as usize].unwrap().lhs; let idx = match builder.tree.nodes.get_node(AstNode::new(ast_node).unwrap()) { Tag::FunctionDecl { proto, .. } => { variant!(builder.tree.nodes.get_node(*proto) => Tag::FunctionProto { name,..}); variant!(builder.tree.nodes.get_node(*name) => Tag::Ident { name }); *name } Tag::GlobalDecl { name, .. } => { variant!(builder.tree.nodes.get_node(*name) => Tag::Ident { name }); *name } _ => { unreachable!() } }; builder.ir.data[fix as usize] = Some(idx.into()); } builder } } impl<'tree, 'ir> IRBuilder<'tree, 'ir> { fn render_node( &self, w: &mut W, node: Node, indent: u32, ) -> core::fmt::Result { let data = self.ir.data[node as usize] .clone() .unwrap_or(Data::new(0, 0)); let inst = self.ir.nodes[node as usize]; match inst { Inst::Label => { let label = self.tree.strings.get_str(data.as_index()); writeln_indented!(indent - 1, w, "%{} = label \"{label}\":", node)?; } Inst::FunctionStart => { let label = self.tree.strings.get_str(data.as_index()); writeln_indented!(indent - 1, w, "%{} = func {label}:", node)?; } Inst::FunctionEnd => { writeln_indented!(indent - 1, w, "end func")?; } Inst::Parameter(ty) => { let (size, align) = data.as_lhs_rhs(); writeln_indented!( indent, w, "%{} = param {ty} (size: {}, align: {})", node, size, align )?; } Inst::Argument(ty) => { let lhs = data.lhs; writeln_indented!(indent, w, "%{node} = argument {ty} %{lhs}",)?; } Inst::InlineType(_) => {} Inst::Call(func) => { let (start, end) = data.as_lhs_rhs(); variant!(&self.ir.nodes[end as usize -1] => Inst::InlineType(ty)); write_indented!(indent, w, "%{node} = {ty} call %{func}(",)?; if start + 1 == end { } else { for arg in start..(end - 1) { let arg = self.ir.data[arg as usize].unwrap().lhs; write_indented!(indent, w, "%{arg}, ")?; } } writeln_indented!(indent, w, ")")?; } Inst::ConstantU32 => { writeln_indented!(indent, w, "%{} = const i32 {}", node, data.as_u32())?; } Inst::ConstantU64 => { writeln_indented!(indent, w, "%{} = const i64 {}", node, data.as_u64())?; } Inst::ConstantMultiByte => { let value = self.tree.strings.get_bytes(data.as_index()); writeln_indented!(indent, w, "%{} = const bytes {:x?}", node, value)?; } Inst::Add(ty) => { let (lhs, rhs) = data.as_lhs_rhs(); writeln_indented!(indent, w, "%{} = add_{ty}(%{} + %{})", node, lhs, rhs)?; } Inst::Sub(ty) => { let (lhs, rhs) = data.as_lhs_rhs(); writeln_indented!(indent, w, "%{} = sub_{ty}(%{} - %{})", node, lhs, rhs)?; } Inst::Eq(ty) => { let (lhs, rhs) = data.as_lhs_rhs(); writeln_indented!(indent, w, "%{} = eq_{ty}(%{} == %{})", node, lhs, rhs)?; } Inst::Neq(ty) => { let (lhs, rhs) = data.as_lhs_rhs(); writeln_indented!(indent, w, "%{} = neq_{ty}(%{} != %{})", node, lhs, rhs)?; } Inst::Gt(ty) => { let (lhs, rhs) = data.as_lhs_rhs(); writeln_indented!(indent, w, "%{} = gt_{ty}(%{} > %{})", node, lhs, rhs)?; } Inst::Lt(ty) => { let (lhs, rhs) = data.as_lhs_rhs(); writeln_indented!(indent, w, "%{} = lt_{ty}(%{} < %{})", node, lhs, rhs)?; } Inst::Ge(ty) => { let (lhs, rhs) = data.as_lhs_rhs(); writeln_indented!(indent, w, "%{} = ge_{ty}(%{} >= %{})", node, lhs, rhs)?; } Inst::Le(ty) => { let (lhs, rhs) = data.as_lhs_rhs(); writeln_indented!(indent, w, "%{} = le_{ty}(%{} <= %{})", node, lhs, rhs)?; } Inst::Mul(ty) => { let (lhs, rhs) = data.as_lhs_rhs(); writeln_indented!(indent, w, "%{} = mul_{ty}(%{} * %{})", node, lhs, rhs)?; } Inst::Negate(ty) => { writeln_indented!(indent, w, "%{} = negate_{ty}(%{})", node, data.lhs)?; } Inst::Not(ty) => { writeln_indented!(indent, w, "%{} = bitwise_not_{ty}(%{})", node, data.lhs)?; } Inst::ExplicitCast(from, to) => { writeln_indented!(indent, w, "%{} = cast_{from}_to_{to}(%{})", node, data.lhs)?; } Inst::ShiftLeft(ty) => { writeln_indented!( indent, w, "%{} = shl_{ty}(%{} << %{})", node, data.lhs, data.rhs )?; } Inst::ShiftRight(ty) => { writeln_indented!( indent, w, "%{} = shr_{ty}(%{} >> %{})", node, data.lhs, data.rhs )?; } Inst::ReturnValue(ty) => { writeln_indented!(indent, w, "%{} = return {ty} %{}", node, data.lhs)?; } Inst::Return => { writeln_indented!(indent, w, "%{} = return", node)?; } Inst::Alloca => { let (size, align) = data.as_lhs_rhs(); writeln_indented!(indent, w, "%{} = alloca {size} (align: {align})", node)?; } Inst::GetElementPtr(ty) => { let (ptr, idx) = data.as_lhs_rhs(); writeln_indented!( indent, w, "%{node} = getelementptr {ty}, ptr: %{}, idx: {}", ptr, idx )?; } Inst::Load(ty) => { let source = data.lhs; writeln_indented!(indent, w, "%{} = load {ty}, %{source}", node)?; } Inst::Store(ty) => { let (src, dst) = data.as_lhs_rhs(); writeln_indented!(indent, w, "%{} = store {ty}, ptr %{dst}, %{src}", node)?; } Inst::ExternRef(ty) => { let idx = data.as_index(); writeln_indented!( indent, w, "%{node} = extern reference {ty} '{}'", self.tree.strings.get_str(idx) )?; } Inst::Branch(condition) => { let (lhs, rhs) = data.as_lhs_rhs(); writeln_indented!( indent, w, "%{node} = br bool %{condition}, [%{lhs}, %{rhs}]" )?; } Inst::Jump => { let lhs = data.lhs; writeln_indented!(indent, w, "%{node} = jmp %{lhs}")?; } Inst::Phi2(ty) => { let (lhs, rhs) = data.as_lhs_rhs(); writeln_indented!(indent, w, "%{node} = phi [{ty} %{lhs}, {ty} %{rhs}]")?; } Inst::GlobalConstant(name, ty) => { let label = self.tree.strings.get_str(name); let value = data.lhs; writeln_indented!(indent, w, "%{node} = global const '{label}' {ty} %{value}")?; } _ => { unimplemented!("{inst:?} rendering unimplemented") } } Ok(()) } pub fn render(&self, w: &mut W) -> core::fmt::Result { for node in 0..self.ir.nodes.len() { self.render_node(w, node as u32, 1)?; } Ok(()) } } struct IRIter<'a> { ir: &'a IR, offset: usize, item: Option<(Inst, Option)>, } impl<'a> IRIter<'a> { fn node(&self) -> u32 { self.offset as Node } } impl<'a> Iterator for IRIter<'a> { type Item = (Inst, Option); fn next(&mut self) -> Option { let inst = self.ir.nodes.get(self.offset)?; let data = self.ir.data.get(self.offset)?; self.offset += 1; self.item = Some((*inst, *data)); Some((*inst, *data)) } } use crate::mir; pub struct MirBuilder<'a> { ir: IRIter<'a>, pub strings: StringTable, pub functions: HashMap, pub globals: HashMap, } struct IrToMirMapping<'a> { ir: &'a IR, mapping: BTreeMap, } impl<'a> IrToMirMapping<'a> { fn new(ir: &'a IR) -> Self { Self { ir, mapping: BTreeMap::new(), } } fn insert(&mut self, ir: Node, mir: mir::NodeRef) { self.mapping.insert(ir, mir); } fn get(&mut self, mir: &mut mir::Mir, ir: Node) -> Option { if let Some(&mir) = self.mapping.get(&ir) { return Some(mir); } match self.ir.nodes[ir as usize] { Inst::GlobalConstant(name, ty) => { eprintln!("does this even get hit anymore???????????????????//"); let ext = mir::NodeRef(mir.gen_extern(Some(ty.mir_type()), name)); self.insert(ir, ext); Some(ext) } _ => None, } } } impl<'a> MirBuilder<'a> { pub fn new(ir: &'a IR, strings: StringTable) -> MirBuilder<'a> { Self { ir: IRIter { ir, offset: 0, item: None, }, strings, functions: HashMap::new(), globals: HashMap::new(), } } fn build_function(&mut self, name: StringsIndex) { let mut mir = mir::Mir::new(name); let mut mapping = IrToMirMapping::new(&self.ir.ir); // map of label -> unresolved mir jump or branch instruction // stored as a tree of (label, unresolved) #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] enum LeftRight { Left(mir::NodeRef), Right(mir::NodeRef), } impl LeftRight { fn noderef(self) -> mir::NodeRef { match self { LeftRight::Left(noderef) => noderef, LeftRight::Right(noderef) => noderef, } } } let mut unresolved_jumps_branches = BTreeSet::<(Node, LeftRight)>::new(); loop { let ir_node = self.ir.node(); let Some((inst, data)) = self.ir.next() else { break; }; let node = match inst { Inst::FunctionStart => { self.ir.offset -= 1; break; } Inst::FunctionEnd => { break; } Inst::Label => { let label = mir.gen_label(data.unwrap().as_index()); let range = unresolved_jumps_branches .range( (ir_node, LeftRight::Left(mir::NodeRef::MIN)) ..=(ir_node, LeftRight::Right(mir::NodeRef::MAX)), ) .map(|(_, n)| n) .cloned() .collect::>(); for unresolved in range { unresolved_jumps_branches.remove(&(ir_node, unresolved)); let mir_node = unresolved.noderef(); let (inst, data) = mir.get_node_mut(mir_node); match inst { mir::Inst::Jump => { *data = mir::Data::node(label); } mir::Inst::Branch(_) => { let (lhs, rhs) = data.as_binary_noderefs(); *data = match unresolved { LeftRight::Left(_) => mir::Data::binary(label, rhs.0), LeftRight::Right(_) => mir::Data::binary(lhs.0, label), }; } _ => { unreachable!() } } } label } Inst::ConstantU32 => mir.push( mir::Inst::ConstantDWord, mir::Data::imm32(data.unwrap().as_u32()), ), Inst::ConstantU64 => mir.push( mir::Inst::ConstantQWord, mir::Data::imm64(data.unwrap().as_u64()), ), Inst::ConstantMultiByte => { let bytes = self.strings.get_bytes(data.unwrap().as_index()); let mut buf = [0u8; 8]; match bytes.len() { 1 => mir.gen_u8(bytes[0]), 2 => mir.gen_u16(u16::from_le_bytes(bytes[..2].try_into().unwrap())), 3..=4 => { buf[..bytes.len()].copy_from_slice(bytes); mir.gen_u32(u32::from_le_bytes(buf[..4].try_into().unwrap())) } 5..=8 => { buf[..bytes.len()].copy_from_slice(bytes); mir.gen_u64(u64::from_le_bytes(buf[..8].try_into().unwrap())) } _ => { unimplemented!( "constants larger than 8 bytes are not currently supported!" ) } } } Inst::Alloca => { let (l, r) = data.unwrap().as_lhs_rhs(); mir.gen_alloca(l, r) } Inst::Load(ty) => { let ty = mir::Type::from_bytesize_int(ty.size()); let src = mapping.get(&mut mir, data.unwrap().as_u32()).unwrap().0; mir.gen_load(ty, src) } Inst::Store(ty) => { let ty = mir::Type::from_bytesize_int(ty.size()); let (src, dst) = data.unwrap().as_lhs_rhs(); let src = mapping.get(&mut mir, src).unwrap().0; let dst = mapping.get(&mut mir, dst).unwrap().0; mir.gen_store(ty, src, dst) } Inst::GetElementPtr(ty) => { let ty = mir::Type::from_bytesize_int(ty.size()); let (ptr, idx) = data.unwrap().as_lhs_rhs(); let src = mapping.get(&mut mir, ptr).unwrap().0; mir.gen_get_element_ptr(ty, src, idx) } Inst::Parameter(ty) => { // let (size, _) = data.unwrap().as_lhs_rhs(); mir.gen_param(ty.into()) } Inst::Eq(ty) | Inst::Neq(ty) | Inst::Gt(ty) | Inst::Lt(ty) | Inst::Ge(ty) | Inst::Le(ty) => { let (src, dst) = data.unwrap().as_lhs_rhs(); let lhs = mapping.get(&mut mir, src).unwrap().0; let rhs = mapping.get(&mut mir, dst).unwrap().0; #[cfg_attr(rustfmt, rustfmt::skip)] let (ord, invert)= match inst { Inst::Eq(_) => (Ordering::Equal, false), Inst::Neq(_) => (Ordering::Equal, true), Inst::Gt(_) => (Ordering::Greater, false), Inst::Le(_) => (Ordering::Greater, true), Inst::Lt(_) => (Ordering::Less, false), Inst::Ge(_) => (Ordering::Less, true), _ => unreachable!(), }; mir.gen_cmp_byte(ty.mir_type(), ty.is_signed(), ord, invert, lhs, rhs) } Inst::Add(ty) => { let (src, dst) = data.unwrap().as_lhs_rhs(); let lhs = mapping.get(&mut mir, src).unwrap().0; let rhs = mapping.get(&mut mir, dst).unwrap().0; match ty { Type2::Integral(signed, bits) => match bits { 8 => mir.gen_add(mir::Type::Byte, lhs, rhs), 16 => mir.gen_add(mir::Type::Word, lhs, rhs), 32 => mir.gen_add(mir::Type::DWord, lhs, rhs), 64 => mir.gen_add(mir::Type::QWord, lhs, rhs), 64.. => { unimplemented!() } bits => { let ty = mir::Type::from_bitsize_int(bits as u32); let sum = mir.gen_add(ty, lhs, rhs); mir.gen_truncate_integer(sum, ty, signed, bits) } }, Type2::Binary32 => mir.gen_add(mir::Type::SinglePrecision, lhs, rhs), Type2::Binary64 => mir.gen_add(mir::Type::DoublePrecision, lhs, rhs), Type2::Pointer => mir.gen_add(mir::Type::QWord, lhs, rhs), _ => unreachable!(), } } Inst::Sub(ty) => { let (src, dst) = data.unwrap().as_lhs_rhs(); let lhs = mapping.get(&mut mir, src).unwrap().0; let rhs = mapping.get(&mut mir, dst).unwrap().0; let unalignment = ty.mir_unalignment(); let ty = ty.mir_type(); let sum = mir.gen_sub(ty, lhs, rhs); if let Some((signed, bits)) = unalignment { mir.gen_truncate_integer(sum, ty, signed, bits) } else { sum } } Inst::Mul(ty) => { let (lhs, rhs) = data.unwrap().as_lhs_rhs(); let unalignment = ty.mir_unalignment(); let signed = ty.is_signed(); let ty = ty.mir_type(); let lhs = mapping.get(&mut mir, lhs).unwrap().0; let rhs = mapping.get(&mut mir, rhs).unwrap().0; let sum = mir.gen_mul(ty, signed, lhs, rhs); if let Some((signed, bits)) = unalignment { mir.gen_truncate_integer(sum, ty, signed, bits) } else { sum } } Inst::Div(ty) => { let (lhs, rhs) = data.unwrap().as_lhs_rhs(); let unalignment = ty.mir_unalignment(); let signed = ty.is_signed(); let ty = ty.mir_type(); let lhs = mapping.get(&mut mir, lhs).unwrap().0; let rhs = mapping.get(&mut mir, rhs).unwrap().0; let sum = mir.gen_div(ty, signed, lhs, rhs); if let Some((signed, bits)) = unalignment { mir.gen_truncate_integer(sum, ty, signed, bits) } else { sum } } Inst::Rem(ty) => { let (lhs, rhs) = data.unwrap().as_lhs_rhs(); let unalignment = ty.mir_unalignment(); let signed = ty.is_signed(); let ty = ty.mir_type(); let lhs = mapping.get(&mut mir, lhs).unwrap().0; let rhs = mapping.get(&mut mir, rhs).unwrap().0; let sum = mir.gen_rem(ty, signed, lhs, rhs); if let Some((signed, bits)) = unalignment { mir.gen_truncate_integer(sum, ty, signed, bits) } else { sum } } Inst::BitAnd(ty) => { let (lhs, rhs) = data.unwrap().as_lhs_rhs(); let unalignment = ty.mir_unalignment(); let ty = ty.mir_type(); let (lhs, rhs) = if self.ir.ir.nodes[lhs as usize].is_constant() { (rhs, lhs) } else { (lhs, rhs) }; let lhs = mapping.get(&mut mir, lhs).unwrap().0; let rhs = mapping.get(&mut mir, rhs).unwrap().0; let sum = mir.gen_bitand(ty, lhs, rhs); if let Some((signed, bits)) = unalignment { mir.gen_truncate_integer(sum, ty, signed, bits) } else { sum } } Inst::BitOr(ty) => { let (lhs, rhs) = data.unwrap().as_lhs_rhs(); let unalignment = ty.mir_unalignment(); let ty = ty.mir_type(); let (lhs, rhs) = if self.ir.ir.nodes[lhs as usize].is_constant() { (rhs, lhs) } else { (lhs, rhs) }; let lhs = mapping.get(&mut mir, lhs).unwrap().0; let rhs = mapping.get(&mut mir, rhs).unwrap().0; let sum = mir.gen_bitor(ty, lhs, rhs); if let Some((signed, bits)) = unalignment { mir.gen_truncate_integer(sum, ty, signed, bits) } else { sum } } Inst::BitXOr(ty) => { let (lhs, rhs) = data.unwrap().as_lhs_rhs(); let unalignment = ty.mir_unalignment(); let ty = ty.mir_type(); let (lhs, rhs) = if self.ir.ir.nodes[lhs as usize].is_constant() { (rhs, lhs) } else { (lhs, rhs) }; let lhs = mapping.get(&mut mir, lhs).unwrap().0; let rhs = mapping.get(&mut mir, rhs).unwrap().0; let sum = mir.gen_bitxor(ty, lhs, rhs); if let Some((signed, bits)) = unalignment { mir.gen_truncate_integer(sum, ty, signed, bits) } else { sum } } Inst::ShiftLeft(ty) => { let (src, dst) = data.unwrap().as_lhs_rhs(); let lhs = mapping.get(&mut mir, src).unwrap().0; let rhs = mapping.get(&mut mir, dst).unwrap().0; // TODO: check rhs type and pass it to gen_sh{l,r}? let rhs = mir.gen_truncate_integer(rhs, ty.into(), false, 8); match ty { Type2::Integral(signed, bits) => match bits { 8 => mir.gen_shl(mir::Type::Byte, lhs, rhs), 16 => mir.gen_shl(mir::Type::Word, lhs, rhs), 32 => mir.gen_shl(mir::Type::DWord, lhs, rhs), 64 => mir.gen_shl(mir::Type::QWord, lhs, rhs), 64.. => { unimplemented!() } bits => { let ty = mir::Type::from_bitsize_int(bits as u32); let sum = mir.gen_shl(ty, lhs, rhs); mir.gen_truncate_integer(sum, ty, signed, bits) } }, _ => unreachable!(), } } Inst::ShiftRight(ty) => { let (src, dst) = data.unwrap().as_lhs_rhs(); let lhs = mapping.get(&mut mir, src).unwrap().0; let rhs = mapping.get(&mut mir, dst).unwrap().0; match ty { Type2::Integral(signed, bits) => match bits { 8 | 16 | 32 | 64 => { let ty = mir::Type::from_bitsize_int(bits as u32); if signed { mir.gen_sar(ty, lhs, rhs) } else { mir.gen_shr(ty, lhs, rhs) } } 64.. => { unimplemented!() } bits => { let ty = mir::Type::from_bitsize_int(bits as u32); let sum = if signed { mir.gen_sar(ty, lhs, rhs) } else { mir.gen_shr(ty, lhs, rhs) }; mir.gen_truncate_integer(sum, ty, signed, bits) } }, _ => unreachable!(), } } Inst::Not(ty) => { let lhs = data.unwrap().as_u32(); let unalignment = ty.mir_unalignment(); let ty = ty.mir_type(); let lhs = mapping.get(&mut mir, lhs).unwrap().0; let sum = mir.gen_not(ty, lhs); if let Some((signed, bits)) = unalignment { mir.gen_truncate_integer(sum, ty, signed, bits) } else { sum } } Inst::Negate(ty) => { let lhs = data.unwrap().as_u32(); let unalignment = ty.mir_unalignment(); let ty = ty.mir_type(); let lhs = mapping.get(&mut mir, lhs).unwrap().0; let sum = mir.gen_negate(ty, lhs); if let Some((signed, bits)) = unalignment { mir.gen_truncate_integer(sum, ty, signed, bits) } else { sum } } Inst::ExplicitCast(from, to) => { let lhs = data.unwrap().as_u32(); let from_mir = from.mir_type(); let to_mir = to.mir_type(); let lhs = mapping.get(&mut mir, lhs).unwrap().0; match (from, to) { (Type2::Integral(a_signed, a), Type2::Integral(b_signed, b)) => { if a > b { mir.gen_truncate_integer(lhs, to_mir, b_signed, b) } else if a < b { mir.gen_extend_integer( lhs, IntegralType::new(a_signed, a), IntegralType::new(b_signed, b), ) } else { unreachable!() } } (Type2::Integral(_, _), Type2::Bool) => { let is_zero = mir.gen_is_zero(from_mir, lhs); mir.gen_negate(mir::Type::Byte, is_zero) } (Type2::Bool, Type2::Integral(b_signed, b)) => mir.gen_extend_integer( lhs, IntegralType::u1(), IntegralType::new(b_signed, b), ), _ => unimplemented!(), } } Inst::ReturnValue(ty) => { let src = data.unwrap().as_u32(); let src = mapping.get(&mut mir, src).unwrap().0; mir.gen_ret_val(ty.mir_type(), src) } Inst::Return => mir.gen_ret(), Inst::Jump => { let label = data.unwrap().as_u32(); let jmp = mir.gen_jmp(label); let label = match mapping.get(&mut mir, label) { Some(label) => label.0, None => { unresolved_jumps_branches .insert((label, LeftRight::Left(mir::NodeRef(jmp)))); 0 } }; mir.set_node_data(mir::NodeRef(jmp), mir::Data::node(label)); jmp } Inst::Branch(condition) => { let condition = mapping.get(&mut mir, condition).unwrap().0; let (lhs, rhs) = data.unwrap().as_lhs_rhs(); let br = mir.gen_branch(condition, lhs, rhs); let lhs = match mapping.get(&mut mir, lhs) { Some(n) => n.0, None => { unresolved_jumps_branches .insert((lhs, LeftRight::Left(mir::NodeRef(br)))); 0 } }; let rhs = match mapping.get(&mut mir, rhs) { Some(n) => n.0, None => { unresolved_jumps_branches .insert((rhs, LeftRight::Right(mir::NodeRef(br)))); 0 } }; mir.set_node_data(mir::NodeRef(br), mir::Data::binary(lhs, rhs)); br } Inst::Call(func) => { let f = mapping.get(&mut mir, func).unwrap().0; let ir = self.ir.ir; let (start, end) = data.unwrap().as_lhs_rhs(); variant!(&ir.nodes[end as usize -1] => Inst::InlineType(ty)); let args = (start..(end - 1)) .map(|arg| { variant!(&ir.nodes[arg as usize] => Inst::Argument(ty)); let arg = ir.data[arg as usize].unwrap().lhs; let arg = mapping.get(&mut mir, arg).unwrap().0; (ty.mir_type(), arg) }) .collect::>(); mir.gen_call(ty.mir_type(), f, args) } Inst::Phi2(ty) => { let (src, dst) = data.unwrap().as_lhs_rhs(); let lhs = mapping.get(&mut mir, src).unwrap().0; let rhs = mapping.get(&mut mir, dst).unwrap().0; mir.gen_phi2(ty.mir_type(), lhs, rhs) } Inst::GlobalConstant(name, ty) => { let ext = mir.gen_extern(Some(ty.mir_type()), name); ext } Inst::Argument(_) | Inst::InlineType(_) => { continue; } Inst::ExternRef(ty) => { mir.gen_extern(Some(ty.mir_type()), data.unwrap().as_index()) } #[allow(unreachable_patterns)] _ => { eprintln!("ir inst {inst:?} not supported in mir translation"); unimplemented!() } }; mapping.insert(ir_node, mir::NodeRef(node)); } self.functions.insert(name, mir); } pub fn build(&mut self) { loop { let Some((inst, data)) = self.ir.next() else { break; }; match inst { Inst::FunctionStart => self.build_function(data.unwrap().as_index()), Inst::GlobalConstant(name, ..) => { let mut mir = mir::Mir::new(name); let value = data.unwrap().lhs; let data = self.ir.ir.data[value as usize]; let inst = self.ir.ir.nodes[value as usize]; let _value = match inst { Inst::ConstantU32 => mir.push( mir::Inst::ConstantDWord, mir::Data::imm32(data.unwrap().as_u32()), ), Inst::ConstantU64 => mir.push( mir::Inst::ConstantQWord, mir::Data::imm64(data.unwrap().as_u64()), ), Inst::ConstantMultiByte => { let bytes = self.strings.get_bytes(data.unwrap().as_index()); let mut buf = [0u8; 8]; match bytes.len() { 1 => mir.gen_u8(bytes[0]), 2 => { mir.gen_u16(u16::from_le_bytes(bytes[..2].try_into().unwrap())) } 3..=4 => { buf[..bytes.len()].copy_from_slice(bytes); mir.gen_u32(u32::from_le_bytes(buf[..4].try_into().unwrap())) } 5..=8 => { buf[..bytes.len()].copy_from_slice(bytes); mir.gen_u64(u64::from_le_bytes(buf[..8].try_into().unwrap())) } _ => { unimplemented!( "constants larger than 8 bytes are not currently supported!" ) } } } _ => { unimplemented!() } }; self.globals.insert(name, mir); } _ => {} } } } } #[cfg(test)] mod tests { use crate::lexer::Tokenizer; use super::*; #[test] fn mir_u10() { let src = " fn u10(x: i10) -> i10 { 5i10 * 3i10 } "; let tokens = Tokenizer::new(src.as_bytes()).unwrap(); let mut tree = Tree::new(); tree.parse(tokens.iter()).unwrap(); let mut buf = String::new(); tree.render(&mut buf).unwrap(); println!("AST:\n{buf}"); let mut ir = IR::new(); let builder = ir.build(&mut tree); let mut buf = String::new(); builder.render(&mut buf).unwrap(); println!("IR:\n{buf}"); let mut mir = MirBuilder::new(&ir, tree.strings); mir.build(); } }