SeaLang/src/triples.rs
2024-09-02 16:52:48 +02:00

1648 lines
59 KiB
Rust

#![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<Node>), // list of references to `Node(_)`
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Type2 {
Integral(bool, u16),
Binary32,
Binary64,
Bool,
Pointer,
}
impl Into<mir::Type> 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<Self> {
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<Type> 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<u32> for Data {
fn from(value: u32) -> Self {
Self { lhs: value, rhs: 0 }
}
}
impl From<u64> 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<crate::string_table::ImmOrIndex> 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<crate::string_table::Index> 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<AstNode, Type>,
lookup: HashMap<AstNode, NodeOrList>,
fixup: Vec<Node>,
}
impl core::ops::Index<Node> 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<Type> {
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::<Vec<_>>();
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<Inst>,
data: Vec<Option<Data>>,
}
impl IR {
pub fn new() -> Self {
Self {
nodes: Vec::new(),
data: Vec::new(),
}
}
fn push(&mut self, inst: Inst, data: Option<Data>) -> 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<W: core::fmt::Write>(
&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<W: core::fmt::Write>(&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<Data>)>,
}
impl<'a> IRIter<'a> {
fn node(&self) -> u32 {
self.offset as Node
}
}
impl<'a> Iterator for IRIter<'a> {
type Item = (Inst, Option<Data>);
fn next(&mut self) -> Option<Self::Item> {
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<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) => {
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::<Vec<_>>();
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::<Vec<_>>();
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();
}
}