From 4424ef875d9918002772c777e25b8675ceaa0e8b Mon Sep 17 00:00:00 2001 From: Janis Date: Mon, 2 Sep 2024 12:59:17 +0200 Subject: [PATCH] global symbols --- src/bin/main.rs | 19 ++- src/mir.rs | 227 +++++++++++------------------------ src/parser.rs | 94 ++++++++------- src/triples.rs | 311 ++++++++++++++++++++++++++++++------------------ 4 files changed, 336 insertions(+), 315 deletions(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index 371047d..61fcfbc 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -77,13 +77,28 @@ fn main() { mir.build(); let MirBuilder { - strings, functions, .. + globals, + strings, + functions, + .. } = mir; println!(".intel_syntax"); println!(".text"); + for (_name, mir) in globals { + let assembly = mir + .assemble(&strings) + .unwrap() + .finish_constants(&strings) + .unwrap(); + println!("{assembly}"); + } for (_name, mir) in functions { - let assembly = mir.assemble(&strings).unwrap(); + let assembly = mir + .assemble(&strings) + .unwrap() + .finish_as_function(&strings) + .unwrap(); println!("{assembly}"); } } diff --git a/src/mir.rs b/src/mir.rs index 6aaaa59..bb4d63e 100644 --- a/src/mir.rs +++ b/src/mir.rs @@ -2,14 +2,13 @@ use std::cmp::Ordering; use std::collections::btree_map::Entry; -use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet}; use std::fmt::Display; use std::hash::{Hash, Hasher}; use std::iter::FusedIterator; use std::u32; use itertools::Itertools; -use liveness::LivenessBuilder; use crate::asm::amd64::Register; use crate::ast::IntegralType; @@ -138,8 +137,8 @@ pub enum Inst { ConstantDoublePrecision, /// src LoadRegister(Type), // hint for loading value into register - /// ast-node - ExternRef, + /// index + ExternRef(Option), // might have a type, or might be a name only, /// size, align Alloca, /// src @@ -224,9 +223,9 @@ pub enum Inst { impl Inst { fn value_type(&self) -> Option { match self { + Inst::ExternRef(ty) => *ty, Inst::Label | Inst::ConstantBytes - | Inst::ExternRef | Inst::Alloca | Inst::ReturnValue(_) | Inst::Store(_) @@ -290,7 +289,7 @@ impl Inst { | Inst::ConstantQWord | Inst::ConstantSinglePrecision | Inst::ConstantDoublePrecision - | Inst::ExternRef + | Inst::ExternRef(_) | Inst::Alloca | Inst::Store(_) | Inst::ReturnValue(_) @@ -950,6 +949,9 @@ impl Mir { pub fn gen_label(&mut self, name: StringsIndex) -> u32 { self.push(Inst::Label, Data::index(name)) } + pub fn gen_extern(&mut self, ty: Option, name: StringsIndex) -> u32 { + self.push(Inst::ExternRef(ty), Data::index(name)) + } pub fn gen_alloca(&mut self, size: u32, align: u32) -> u32 { self.push(Inst::Alloca, Data::binary(size, align)) } @@ -1190,7 +1192,13 @@ impl Mir { let src = data.as_node(); writeln!(w, "%{node} = load register {ty} %{src}") } - Inst::ExternRef => writeln!(w, "%{node} = extern %%{}", data.as_node()), + Inst::ExternRef(ty) => { + write!(w, "%{node} = extern ")?; + if let Some(ty) = ty { + write!(w, "{ty} ")?; + } + writeln!(w, "{}", strings.get_str(data.as_index())) + } Inst::Alloca => { let (size, align) = data.as_binary(); writeln!(w, "%{node} = alloca {size}, {align}") @@ -1497,9 +1505,6 @@ pub mod liveness { let branch_graph = BranchGraph::from_edges(branch_graph_edges); - eprintln!("branch graph: {branch_graph:?}"); - eprintln!("references: {references:?}"); - let mut intervals = mir .indices() .filter_map(|node| { @@ -1530,9 +1535,6 @@ pub mod liveness { }) .collect::>(); - eprintln!("intervals: {intervals:#?}"); - eprintln!("edges: {edges:#?}"); - // build interference graph with mutliple branches. // BTreeMap type Edge = (NodeRef, NodeRef); @@ -1562,8 +1564,6 @@ pub mod liveness { } } - eprintln!("per_branch_edges: {per_branch_edges:?}"); - let branches = per_branch_edges .into_iter() .map(|(range, edges)| { @@ -1573,7 +1573,6 @@ pub mod liveness { ) }) .collect::>(); - eprintln!("branches: {branches:#?}"); Self { branches, @@ -1651,12 +1650,6 @@ pub mod liveness { } } - #[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] - struct LivenessReference { - node: NodeRef, - referenced_by: NodeRef, - } - pub struct BranchedLivenessBuilder<'a> { mir: &'a Mir, // tree of (node, referenced_by) pairs. @@ -1785,21 +1778,6 @@ pub mod liveness { } } - pub struct LivenessBuilder<'a> { - mir: &'a Mir, - // tree of (node, referenced_by) pairs. - // a full range for each node is (node, NodeRef::MIN)..(node, NodeRef::MAX) - // QUESTION: do I want to treat every interval in this tree a separate node to color? - // or just the (node, NodeRef::MIN)..(node, NodeRef::MAX) range? - // references: BTreeSet<(NodeRef, NodeRef)>, - inference_graph: petgraph::graph::UnGraph<(), ()>, - // list of preferred colors by nodes, either because they output to some - // register like mul/div or because the write to one of their inputs. - // interesting to consider optimisations like i >>= s being turned into shl - // mem, cl, while i >> s is turned into shl reg, cl ... - // preferred_color: BTreeMap, - } - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] enum Color { Tentative(Register), @@ -1814,103 +1792,6 @@ pub mod liveness { } } - impl<'a> LivenessBuilder<'a> { - pub fn new(mir: &'a Mir) -> Self { - let references = build_reference_tree(mir); - - let intervals = mir.indices().filter_map(|node| { - let interval = references - .range(node.into_reference_range()) - .map(|&(_, to)| to) - .reduce(|acc, to| acc.max(to)); - - interval.map(|max| (node, max)) - }); - - let mut edges = HashSet::<(u32, u32)>::new(); - // eprint!("intervals: ["); - for (from, to) in intervals { - // eprint!("({from})..({to}), "); - if !mir.is_register(from.0) { - continue; - } - for &(other, _) in references.range(from.exclusive_start()..to.exclusive_end()) { - edges.insert((from.0, other.0)); - } - } - // eprintln!("]"); - - let inference_graph = petgraph::graph::UnGraph::<(), ()>::from_edges(edges.into_iter()); - - Self { - mir, - inference_graph, - // references, - } - } - - fn pre_color_colors(&self) -> BTreeMap { - /* do i want to find colors from the bottom up? - * - on one hand, consumers with in/outs like mul/div are below and want their inputs in some specific register - * - on the other hand, producers like mul/div are above and want to dictate their output registers - * i think the answer is coloring mul/div first, specifically, then collapsing from there. - */ - let mut want_colors = BTreeMap::::new(); - - use Register::*; - let mut in_colors = [r9, r8, rcx, rdx, rdi, rsi].to_vec(); - let mut in_colors_sse = [xmm7, xmm6, xmm5, xmm4, xmm3, xmm2, xmm1, xmm0].to_vec(); - - for node in self.mir.indices() { - let want_color = match self.mir.get_node(node).0 { - // - Inst::Parameter(ty) => { - if ty.is_floating() { - in_colors_sse.pop() - } else { - in_colors.pop() - } - } - Inst::ShiftLeft(_) - | Inst::ShiftRightSigned(_) - | Inst::ShiftRightUnsigned(_) => Some(rcx), - Inst::Mul(_) | Inst::Div(_) | Inst::DivSigned(_) => Some(rax), - Inst::Rem(_) | Inst::RemSigned(_) => Some(rdx), - Inst::ReturnValue(ty) => { - if ty.is_floating() { - Some(xmm0) - } else { - Some(rax) - } - } - _ => { - if let Some(dst) = self.mir.dst_node(node) { - // check if there is interference - if self - .inference_graph - .find_edge(dst.0.into(), node.0.into()) - .is_none() - { - // want the same color as our dst node to avoid copying - want_colors.get(&dst).cloned() - } else { - None - } - } else { - None - } - } - }; - - if let Some(color) = want_color { - want_colors.insert(node, color); - } - } - - want_colors - } - } - fn build_reference_tree(mir: &Mir) -> BTreeSet<(NodeRef, NodeRef)> { let mut references = BTreeSet::new(); for node in mir.indices() { @@ -2008,10 +1889,10 @@ pub mod liveness { .cloned() .collect::>(); - eprintln!("coloring %{node_u32}:"); - eprintln!("\twants: {:?}", self.colors.get(&node_ref)); - eprintln!("\tclique: {clique_colors:?}"); - eprintln!("\tcandidates: {colors:?}"); + // eprintln!("coloring %{node_u32}:"); + // eprintln!("\twants: {:?}", self.colors.get(&node_ref)); + // eprintln!("\tclique: {clique_colors:?}"); + // eprintln!("\tcandidates: {colors:?}"); let color = match self.colors.entry(node_ref) { Entry::Vacant(v) => { @@ -2044,7 +1925,6 @@ pub mod liveness { // for any Phi(y_1,y_2, y_n) give y_i the color of Phi // reasonably certain that this will never fail to color all phi nodes the same color. if let Some(inputs) = self.mir.get_phi_inputs(node_ref) { - eprintln!("coloring {inputs:?} {color}"); for node in inputs { _ = self.colors.insert(node, Color::Tentative(color)); } @@ -2139,7 +2019,7 @@ impl Mir { | Inst::ConstantQWord | Inst::ConstantSinglePrecision | Inst::ConstantDoublePrecision - | Inst::ExternRef + | Inst::ExternRef(_) | Inst::Alloca => {} } } @@ -2191,7 +2071,7 @@ impl Mir { | Inst::ConstantQWord | Inst::ConstantSinglePrecision | Inst::ConstantDoublePrecision - | Inst::ExternRef + | Inst::ExternRef(_) | Inst::Alloca | Inst::Jump | Inst::Return @@ -2359,6 +2239,27 @@ impl Function { } } + pub fn finish_as_function(self, strings: &StringTable) -> Result { + let mut buf = String::new(); + self.finish(&mut buf, strings)?; + + Ok(buf) + } + + pub fn finish_constants(self, strings: &StringTable) -> Result { + use core::fmt::Write; + let mut buf = String::new(); + let w = &mut buf; + let name = strings.get_str(self.name).to_owned(); + writeln!(w, ".{name}:")?; + + for (_, contents) in self.constants { + writeln!(w, "{contents}")?; + } + + Ok(buf) + } + #[allow(dead_code)] fn dirty_register(&mut self, reg: Register) { self.dirty_registers.insert(reg); @@ -2436,10 +2337,15 @@ impl Function { writeln!(w, "push {reg}")?; } - writeln!(w, "push rbp")?; - writeln!(w, "mov rbp, rsp")?; - writeln!(w, "sub rsp, {}", self.stack_offset)?; + let needs_frame = !saved_registers.is_empty() && self.stack_offset != 0; + if needs_frame { + writeln!(w, "push rbp")?; + writeln!(w, "mov rbp, rsp")?; + writeln!(w, "sub rsp, {}", self.stack_offset)?; + } + // zero rax because we might only return into some of it + writeln!(w, "test rax, rax")?; write!(w, "{}", self.branches.remove(&NodeRef::MIN).unwrap())?; for (branch, content) in &self.branches { @@ -2448,12 +2354,16 @@ impl Function { } writeln!(w, ".{name}__epilogue:")?; - writeln!(w, "mov rsp, rbp")?; - writeln!(w, "pop rbp")?; - for reg in saved_registers.iter().rev() { - writeln!(w, "pop {reg}")?; + if needs_frame { + writeln!(w, "mov rsp, rbp")?; + writeln!(w, "pop rbp")?; + + for reg in saved_registers.iter().rev() { + writeln!(w, "pop {reg}")?; + } } + writeln!(w, "ret")?; Ok(()) @@ -2542,14 +2452,20 @@ impl Mir { let (offset, size) = *mapping.get(&(node as usize)).unwrap(); ImmRegMem::Mem(StackMem::new(offset, size)) } - Inst::ExternRef => todo!(), + Inst::ExternRef(ty) => { + let ty = ty.unwrap_or(Type::QWord); + ImmRegMem::Rip(RipRelative::Label( + ty, + format!(".{}", strings.get_str(data.as_index())), + )) + } _ => { unreachable!() } } } - pub fn assemble(&self, strings: &StringTable) -> Result { + pub fn assemble(&self, strings: &StringTable) -> Result { use core::fmt::Write; // mapping if (i, (stack_offset, bytes)) for local stack variables let mut mapping = BTreeMap::::new(); @@ -2574,7 +2490,9 @@ impl Mir { | Inst::ConstantByte | Inst::ConstantWord | Inst::ConstantDWord - | Inst::ConstantQWord => {} + | Inst::ConstantQWord => { + func.add_constant_from_inst_and_data(i, inst, data, strings); + } Inst::ConstantSinglePrecision => { let bits = data.as_imm32(); func.add_constant(i, format!(".long {bits}")); @@ -2631,7 +2549,7 @@ impl Mir { writeln!(func.current_branch(), "mov {dst}, {src}",)?; } } - Inst::ExternRef => todo!(), + Inst::ExternRef(_) => {} Inst::Alloca => { let (size, align) = data.as_binary(); let offset = func.alloca(size, align); @@ -3553,10 +3471,7 @@ impl Mir { } } - let mut buf = String::new(); - func.finish(&mut buf, strings)?; - - Ok(buf) + Ok(func) } } diff --git a/src/parser.rs b/src/parser.rs index 1da999e..5104e43 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -374,24 +374,19 @@ impl Tree { }; let name_str = self.strings.get_str(self.ident_index(name)).to_owned(); - let node = { - let node = self.nodes.reserve_node(); - self.st.insert_symbol(&name_str, node, SymbolKind::Var); - node - }; + let decl = self.nodes.reserve_node(); let assignment = if tokens.eat_token(Token::Equal).is_some() { - let expr = self.parse_expr(tokens)?; - Some(self.nodes.push_tag(Tag::Assign { - lhs: node, - rhs: expr, - })) + Some(self.parse_expr(tokens)?) } else { None }; + // insert into symbol table after parsing assignment in case we are shadowing a previous variable binding + self.st.insert_symbol(&name_str, decl, SymbolKind::Var); + self.nodes.set_node( - node, + decl, Tag::VarDecl { let_or_var, name, @@ -401,7 +396,7 @@ impl Tree { ); // return assignment if it exists, to make rendering and visiting easier - Ok(assignment.unwrap_or(node)) + Ok(decl) } /// GLOBAL_DECL <- @@ -418,7 +413,9 @@ impl Tree { }; let name_str = self.get_ident_str(name).unwrap().to_owned(); - let node = { + + // inserting root symbol here is fine, because self-referencing globals are stupid + let decl = { let node = match self.st.find_root_symbol(&name_str) { Some(r) => r.node(), None => self @@ -431,16 +428,10 @@ impl Tree { _ = tokens.expect_token(Token::Equal)?; - let assignment = { - let expr = self.parse_expr(tokens)?; - self.nodes.push_tag(Tag::Assign { - lhs: node, - rhs: expr, - }) - }; + let assignment = self.parse_expr(tokens)?; self.nodes.set_node( - node, + decl, Tag::GlobalDecl { name, explicit_type, @@ -450,7 +441,7 @@ impl Tree { tokens.expect_token(Token::Semi)?; - Ok(assignment) + Ok(decl) } /// PARAMETER <- @@ -1103,23 +1094,24 @@ impl Tree { Tag::VarDecl { name, explicit_type, + assignment, .. - } => { - if let Some(ty) = *explicit_type { - vec![*name, ty] - } else { - vec![*name] - } - } + } => match (*explicit_type, *assignment) { + (None, Some(b)) => vec![*name, b], + (Some(a), None) => vec![*name, a], + (Some(a), Some(b)) => vec![*name, a, b], + _ => unreachable!(), + }, Tag::GlobalDecl { name, explicit_type, + assignment, .. } => { if let Some(ty) = *explicit_type { - vec![*name, ty] + vec![*name, ty, *assignment] } else { - vec![*name] + vec![*name, *assignment] } } &Tag::CallExpr { lhs, rhs } => { @@ -1279,10 +1271,12 @@ impl Tree { let_or_var, name, explicit_type, - .. + assignment, } => { self.render_node(writer, name, indent)?; - explicit_type.map(|ty| self.render_node(writer, ty, indent)); + explicit_type.map(|node| self.render_node(writer, node, indent)); + assignment.map(|node| self.render_node(writer, node, indent)); + write_indented!( indent, writer, @@ -1301,14 +1295,18 @@ impl Tree { if let Some(ty) = explicit_type { write!(writer, ", ty: {}", self.get_typename_str(ty).unwrap())?; } + if let Some(assignment) = assignment { + write!(writer, ", value: %{assignment}")?; + } writeln!(writer, ");")?; Ok(()) } Tag::GlobalDecl { name, explicit_type, - .. + assignment, } => { + self.render_node(writer, assignment, indent)?; self.render_node(writer, name, indent)?; explicit_type.map(|ty| self.render_node(writer, ty, indent)); write_indented!( @@ -1321,6 +1319,7 @@ impl Tree { if let Some(ty) = explicit_type { write!(writer, ", ty: {}", self.get_typename_str(ty).unwrap())?; } + write!(writer, ", value: %{assignment}")?; writeln!(writer, ");")?; Ok(()) } @@ -1674,6 +1673,16 @@ impl Tree { Ok(()) } + fn render2(&mut self, w: &mut W) { + for decl in self.global_decls.clone().iter() { + ast::tree_visitor::Visitor::new( + *decl, + |_: &mut Tree, _: Node| {}, + |_: &mut Tree, _: Node| {}, + ); + } + } + pub fn peer_type_of_nodes_unwrap(&self, lhs: Node, rhs: Node) -> Type { self.peer_type_of_nodes(lhs, rhs).expect({ let at = self.type_of_node(lhs); @@ -1749,8 +1758,7 @@ impl Tree { assignment, // this is a Tag::Assign .. } => { - variant!(self.nodes.get_node(*assignment) => Tag::Assign { rhs ,..}); - let ty = match (explicit_type.as_ref(), rhs) { + let ty = match (explicit_type.as_ref(), assignment) { (None, b) => self.type_of_node(*b), (Some(a), b) => self.peer_type_of_nodes(*a, *b).expect({ let at = self.type_of_node(*a); @@ -1763,13 +1771,10 @@ impl Tree { } Tag::VarDecl { explicit_type, - assignment, // this is a Tag::Assign + assignment, // this is NOT a Tag::Assign .. } => { - let rhs = assignment.map(|n| { - variant!(self.nodes.get_node(n) => Tag::Assign { rhs ,..}); - *rhs - }); + let rhs = *assignment; let ty = match (explicit_type.as_ref(), rhs.as_ref()) { (None, None) => panic!("%{node}: no type specified?"), (None, Some(b)) => self.type_of_node(*b), @@ -1830,7 +1835,7 @@ impl Tree { // simplify tree with compile-time math impl Tree { - fn is_node_comptime(&self, node: Node, check_declrefs: bool) -> bool { + pub fn is_node_comptime(&self, node: Node, check_declrefs: bool) -> bool { match self.nodes.get_node(node) { Tag::Block { statements, @@ -2219,7 +2224,10 @@ impl Tree { Tag::GlobalDecl { assignment, .. } => { self.fold_comptime_with_visitor(*assignment); } - _ => unreachable!(), + _ => { + eprintln!("reached %{decl}:"); + unreachable!() + } } } } diff --git a/src/triples.rs b/src/triples.rs index 24ce946..a7526fc 100644 --- a/src/triples.rs +++ b/src/triples.rs @@ -2,7 +2,7 @@ use std::{ cmp::Ordering, - collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap}, + collections::{BTreeMap, BTreeSet, HashMap}, }; use crate::{ @@ -132,6 +132,11 @@ pub enum Inst { Label, /// index FunctionStart, + /// None + FunctionEnd, // marker + /// Value + /// lhs + GlobalConstant(StringsIndex, Type2), /// u32 ConstantU32, /// lo, hi @@ -372,10 +377,10 @@ impl<'tree, 'ir> IRBuilder<'tree, 'ir> { if value != !0 { let ty = self.tree.type_of_node(*body); self.ir - .push(Inst::ReturnValue(ty.into()), Some(Data::lhs(value))) - } else { - !0 + .push(Inst::ReturnValue(ty.into()), Some(Data::lhs(value))); } + + self.ir.push(Inst::FunctionEnd, None) } Tag::Block { statements, @@ -391,21 +396,66 @@ impl<'tree, 'ir> IRBuilder<'tree, 'ir> { !0 } } - Tag::VarDecl { .. } => { + 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 { .. } => { - // self.ir.push(Inst::Label, { - // variant!(Tag::Ident { name } = self.tree.nodes.get_node(*name)); - // Some((*name).into()) - // }); - unimplemented!() + 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 node = 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") + } + }; + 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 { @@ -423,14 +473,6 @@ impl<'tree, 'ir> IRBuilder<'tree, 'ir> { let lhs = self.visit(*lhs); self.ir.push(Inst::Load(ty.into()), Some(Data::lhs(lhs))) } - 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::Add { lhs, rhs } => { let ty = self.tree.type_of_node(node); let lhs = self.visit(*lhs); @@ -527,15 +569,6 @@ impl<'tree, 'ir> IRBuilder<'tree, 'ir> { self.ir .push(Inst::ShiftRight(ty.into()), Some(Data::new(lhs, rhs))) } - 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::GlobalRef(decl) => self.ir.push(Inst::ExternRef, Some(Data::lhs(decl.get()))), Tag::Ref { lhs } => { let ty = self.tree.type_of_node(*lhs); let lhs = self.visit(*lhs); @@ -671,6 +704,9 @@ impl<'tree, 'ir> IRBuilder<'tree, 'ir> { 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!( @@ -805,6 +841,11 @@ impl<'tree, 'ir> IRBuilder<'tree, 'ir> { 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") } @@ -1102,6 +1143,39 @@ 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) => { + let ext = mir::NodeRef(mir.gen_extern(Some(ty.mir_type()), name)); + self.insert(ir, ext); + Some(ext) + } + _ => None, + } + } } impl<'a> MirBuilder<'a> { @@ -1114,12 +1188,13 @@ impl<'a> MirBuilder<'a> { }, strings, functions: HashMap::new(), + globals: HashMap::new(), } } fn build_function(&mut self, name: StringsIndex) { let mut mir = mir::Mir::new(name); - let mut mapping = BTreeMap::::new(); + 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)] @@ -1147,6 +1222,9 @@ impl<'a> MirBuilder<'a> { self.ir.offset -= 1; break; } + Inst::FunctionEnd => { + break; + } Inst::Label => { let label = mir.gen_label(data.unwrap().as_index()); let range = unresolved_jumps_branches @@ -1220,20 +1298,20 @@ impl<'a> MirBuilder<'a> { } Inst::Load(ty) => { let ty = mir::Type::from_bytesize_int(ty.size()); - let src = *mapping.get(&data.unwrap().as_u32()).unwrap(); + 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(&src).unwrap(); - let dst = *mapping.get(&dst).unwrap(); + 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(&ptr).unwrap(); + let src = mapping.get(&mut mir, ptr).unwrap().0; mir.gen_get_element_ptr(ty, src, idx) } Inst::Parameter(ty) => { @@ -1247,8 +1325,8 @@ impl<'a> MirBuilder<'a> { | Inst::Ge(ty) | Inst::Le(ty) => { let (src, dst) = data.unwrap().as_lhs_rhs(); - let lhs = *mapping.get(&src).unwrap(); - let rhs = *mapping.get(&dst).unwrap(); + 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 { @@ -1265,8 +1343,8 @@ impl<'a> MirBuilder<'a> { } Inst::Add(ty) => { let (src, dst) = data.unwrap().as_lhs_rhs(); - let lhs = *mapping.get(&src).unwrap(); - let rhs = *mapping.get(&dst).unwrap(); + 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 { @@ -1292,8 +1370,8 @@ impl<'a> MirBuilder<'a> { } Inst::Sub(ty) => { let (src, dst) = data.unwrap().as_lhs_rhs(); - let lhs = *mapping.get(&src).unwrap(); - let rhs = *mapping.get(&dst).unwrap(); + 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(); @@ -1312,8 +1390,8 @@ impl<'a> MirBuilder<'a> { let signed = ty.is_signed(); let ty = ty.mir_type(); - let lhs = *mapping.get(&lhs).unwrap(); - let rhs = *mapping.get(&rhs).unwrap(); + 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 { @@ -1329,8 +1407,8 @@ impl<'a> MirBuilder<'a> { let signed = ty.is_signed(); let ty = ty.mir_type(); - let lhs = *mapping.get(&lhs).unwrap(); - let rhs = *mapping.get(&rhs).unwrap(); + 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 { @@ -1346,8 +1424,8 @@ impl<'a> MirBuilder<'a> { let signed = ty.is_signed(); let ty = ty.mir_type(); - let lhs = *mapping.get(&lhs).unwrap(); - let rhs = *mapping.get(&rhs).unwrap(); + 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 { @@ -1368,8 +1446,8 @@ impl<'a> MirBuilder<'a> { (lhs, rhs) }; - let lhs = *mapping.get(&lhs).unwrap(); - let rhs = *mapping.get(&rhs).unwrap(); + 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 { @@ -1390,8 +1468,8 @@ impl<'a> MirBuilder<'a> { (lhs, rhs) }; - let lhs = *mapping.get(&lhs).unwrap(); - let rhs = *mapping.get(&rhs).unwrap(); + 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 { @@ -1412,8 +1490,8 @@ impl<'a> MirBuilder<'a> { (lhs, rhs) }; - let lhs = *mapping.get(&lhs).unwrap(); - let rhs = *mapping.get(&rhs).unwrap(); + 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 { @@ -1424,8 +1502,8 @@ impl<'a> MirBuilder<'a> { } Inst::ShiftLeft(ty) => { let (src, dst) = data.unwrap().as_lhs_rhs(); - let lhs = *mapping.get(&src).unwrap(); - let rhs = *mapping.get(&dst).unwrap(); + 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); @@ -1450,8 +1528,8 @@ impl<'a> MirBuilder<'a> { } Inst::ShiftRight(ty) => { let (src, dst) = data.unwrap().as_lhs_rhs(); - let lhs = *mapping.get(&src).unwrap(); - let rhs = *mapping.get(&dst).unwrap(); + 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 { @@ -1487,7 +1565,7 @@ impl<'a> MirBuilder<'a> { let unalignment = ty.mir_unalignment(); let ty = ty.mir_type(); - let lhs = *mapping.get(&lhs).unwrap(); + let lhs = mapping.get(&mut mir, lhs).unwrap().0; let sum = mir.gen_not(ty, lhs); if let Some((signed, bits)) = unalignment { @@ -1502,7 +1580,7 @@ impl<'a> MirBuilder<'a> { let unalignment = ty.mir_unalignment(); let ty = ty.mir_type(); - let lhs = *mapping.get(&lhs).unwrap(); + let lhs = mapping.get(&mut mir, lhs).unwrap().0; let sum = mir.gen_negate(ty, lhs); if let Some((signed, bits)) = unalignment { @@ -1516,7 +1594,7 @@ impl<'a> MirBuilder<'a> { let from_mir = from.mir_type(); let to_mir = to.mir_type(); - let lhs = *mapping.get(&lhs).unwrap(); + let lhs = mapping.get(&mut mir, lhs).unwrap().0; match (from, to) { (Type2::Integral(a_signed, a), Type2::Integral(b_signed, b)) => { @@ -1546,7 +1624,7 @@ impl<'a> MirBuilder<'a> { } Inst::ReturnValue(ty) => { let src = data.unwrap().as_u32(); - let src = *mapping.get(&src).unwrap(); + let src = mapping.get(&mut mir, src).unwrap().0; mir.gen_ret_val(ty.mir_type(), src) } @@ -1556,8 +1634,8 @@ impl<'a> MirBuilder<'a> { let jmp = mir.gen_jmp(label); - let label = match mapping.get(&label) { - Some(&label) => label, + let label = match mapping.get(&mut mir, label) { + Some(label) => label.0, None => { unresolved_jumps_branches .insert((label, LeftRight::Left(mir::NodeRef(jmp)))); @@ -1570,21 +1648,21 @@ impl<'a> MirBuilder<'a> { jmp } Inst::Branch(condition) => { - let condition = *mapping.get(&condition).unwrap(); + 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(&lhs) { - Some(&n) => n, + 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(&rhs) { - Some(&n) => n, + let rhs = match mapping.get(&mut mir, rhs) { + Some(n) => n.0, None => { unresolved_jumps_branches .insert((rhs, LeftRight::Right(mir::NodeRef(br)))); @@ -1598,18 +1676,24 @@ impl<'a> MirBuilder<'a> { } Inst::Phi2(ty) => { let (src, dst) = data.unwrap().as_lhs_rhs(); - let lhs = *mapping.get(&src).unwrap(); - let rhs = *mapping.get(&dst).unwrap(); + 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 + } #[allow(unreachable_patterns)] _ => { + eprintln!("ir inst {inst:?} not supported in mir translation"); unimplemented!() } }; - mapping.insert(ir_node, node); + mapping.insert(ir_node, mir::NodeRef(node)); } self.functions.insert(name, mir); @@ -1622,6 +1706,50 @@ impl<'a> MirBuilder<'a> { }; 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); + } _ => {} } } @@ -1634,51 +1762,6 @@ mod tests { use super::*; - #[test] - fn mir() { - let src = " -fn inverse_sqrt(x: f32) -> f32 { - let three_halfs: f32 = 1.5f32; - let x2 = x * 0.5f32; - var y = x; - let i = 0x5f3759dfu32 - (*(&y as *u32) >> 1u32); - y = *(&i as *f32); - y = y * (three_halfs - (x2 * y * y)); - - return y; -} -"; - - let tokens = Tokenizer::new(src.as_bytes()).unwrap(); - - let mut tree = Tree::new(); - tree.parse(tokens.iter()).unwrap(); - tree.fold_comptime(); - - 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(); - - let MirBuilder { - strings, functions, .. - } = mir; - - for (_name, mir) in functions { - let assembly = mir.assemble(&strings).unwrap(); - println!("mir:\n{}", mir.display(&strings)); - println!("assembly:\n{assembly}"); - } - } - #[test] fn mir_u10() { let src = "