global symbols

This commit is contained in:
Janis 2024-09-02 12:59:17 +02:00
parent 4b432f404b
commit 4424ef875d
4 changed files with 336 additions and 315 deletions

View file

@ -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}");
}
}

View file

@ -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<Type>), // 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<Type> {
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<Type>, 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::<Vec<_>>();
eprintln!("intervals: {intervals:#?}");
eprintln!("edges: {edges:#?}");
// build interference graph with mutliple branches.
// BTreeMap<MirRange, Interferencegraph>
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::<Vec<_>>();
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<NodeRef, Register>,
}
#[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<NodeRef, Register> {
/* 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::<NodeRef, Register>::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::<BTreeSet<_>>();
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<String, core::fmt::Error> {
let mut buf = String::new();
self.finish(&mut buf, strings)?;
Ok(buf)
}
pub fn finish_constants(self, strings: &StringTable) -> Result<String, core::fmt::Error> {
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}")?;
}
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:")?;
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<String, core::fmt::Error> {
pub fn assemble(&self, strings: &StringTable) -> Result<Function, core::fmt::Error> {
use core::fmt::Write;
// mapping if (i, (stack_offset, bytes)) for local stack variables
let mut mapping = BTreeMap::<usize, (u32, u32)>::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)
}
}

View file

@ -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<W: core::fmt::Write>(&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!()
}
}
}
}

View file

@ -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<StringsIndex, mir::Mir>,
pub globals: HashMap<StringsIndex, mir::Mir>,
}
struct IrToMirMapping<'a> {
ir: &'a IR,
mapping: BTreeMap<Node, mir::NodeRef>,
}
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<mir::NodeRef> {
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::<u32, u32>::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 = "