not really sure, i guess this is an IR?

This commit is contained in:
Janis 2024-08-13 00:00:19 +02:00
parent 02be9bdc26
commit c3157b1355
4 changed files with 764 additions and 35 deletions

View file

@ -186,30 +186,30 @@ pub enum LetOrVar {
Var, Var,
} }
#[derive(Debug)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct IntegralType { pub struct IntegralType {
pub signed: bool, pub signed: bool,
pub bits: u16, pub bits: u16,
} }
impl ToString for IntegralType { impl core::fmt::Display for IntegralType {
fn to_string(&self) -> String { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
format!("{}{}", if self.signed { "i" } else { "u" }, self.bits) write!(f, "{}{}", if self.signed { "i" } else { "u" }, self.bits)
} }
} }
#[derive(Debug)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FloatingType { pub enum FloatingType {
Binary32, Binary32,
Binary64, Binary64,
} }
impl ToString for FloatingType { impl core::fmt::Display for FloatingType {
fn to_string(&self) -> String { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { f.write_str(match self {
FloatingType::Binary32 => "binary32".to_owned(), FloatingType::Binary32 => "binary32",
FloatingType::Binary64 => "binary64".to_owned(), FloatingType::Binary64 => "binary64",
} })
} }
} }
@ -222,6 +222,123 @@ impl IntegralType {
} }
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Type {
Any,
Void,
Bool,
Integer(IntegralType),
Floating(FloatingType),
Pointer {
constness: bool,
pointee: Box<Self>,
},
Fn {
parameter_types: Vec<Self>,
return_type: Box<Self>,
},
}
impl Type {
pub fn void() -> Type {
Self::Void
}
pub fn bool() -> Type {
Self::Void
}
pub fn any() -> Type {
Self::Void
}
pub fn into_ptr(self) -> Type {
Self::Pointer {
constness: false,
pointee: Box::new(self),
}
}
pub fn can_logical_and_or(&self) -> bool {
match self {
Type::Bool => true,
_ => false,
}
}
pub fn can_negate(&self) -> bool {
match self {
Type::Bool | Type::Integer(_) => true,
_ => false,
}
}
pub fn can_bitxor_and_or(&self) -> bool {
match self {
Type::Bool | Type::Integer(_) => true,
_ => false,
}
}
pub fn can_add_sub(&self) -> bool {
match self {
Type::Pointer { .. } | Type::Floating(_) | Type::Integer(_) => true,
_ => false,
}
}
pub fn can_shift(&self) -> bool {
match self {
Type::Integer(_) => true,
_ => false,
}
}
pub fn can_eq(&self) -> bool {
match self {
Type::Pointer { .. } | Type::Bool | Type::Floating(_) | Type::Integer(_) => true,
_ => false,
}
}
pub fn can_cmp(&self) -> bool {
match self {
Type::Pointer { .. } | Type::Floating(_) | Type::Integer(_) => true,
_ => false,
}
}
pub fn can_mul_div_rem(&self) -> bool {
match self {
Type::Floating(_) | Type::Integer(_) => true,
_ => false,
}
}
pub fn is_integer(&self) -> bool {
match self {
Type::Integer(_) => true,
_ => false,
}
}
pub fn remove_ptr(self) -> crate::parser::Result<Self> {
match self {
Type::Pointer { pointee, .. } => Ok(Box::into_inner(pointee)),
_ => Err(crate::parser::Error::TriedToDerefNonPointer),
}
}
pub fn align_of(&self) -> u32 {
self.size_of()
}
pub fn size_of(&self) -> u32 {
match self {
Type::Any => 0,
Type::Void => 0,
Type::Bool => 1,
Type::Integer(t) => t.bits.div_ceil(8) as u32,
Type::Floating(t) => match t {
FloatingType::Binary32 => 4,
FloatingType::Binary64 => 8,
},
Type::Pointer { .. } => 8,
Type::Fn { .. } => 8,
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub enum PrimitiveType { pub enum PrimitiveType {
FloatingType(FloatingType), FloatingType(FloatingType),

View file

@ -1,4 +1,4 @@
#![feature(extract_if, iter_advance_by)] #![feature(extract_if, iter_advance_by, box_into_inner)]
#![allow(dead_code, unused_macros)] #![allow(dead_code, unused_macros)]
pub mod ast; pub mod ast;
@ -6,6 +6,7 @@ pub mod common;
pub mod lexer; pub mod lexer;
pub mod parser; pub mod parser;
pub mod tokens; pub mod tokens;
pub mod triples;
pub fn tokenize<'a>( pub fn tokenize<'a>(
bytes: &'a [u8], bytes: &'a [u8],

View file

@ -3,7 +3,7 @@ use std::collections::HashMap;
use itertools::Itertools; use itertools::Itertools;
use crate::{ use crate::{
ast::{FloatingType, IntegralType, LetOrVar, Node, PrimitiveType, Tag}, ast::{FloatingType, IntegralType, LetOrVar, Node, PrimitiveType, Tag, Type},
common::NextIf, common::NextIf,
lexer::{Radix, TokenIterator}, lexer::{Radix, TokenIterator},
tokens::Token, tokens::Token,
@ -21,17 +21,22 @@ pub enum Error {
ExpectedLetOrVar, ExpectedLetOrVar,
#[error("Dummy message.")] #[error("Dummy message.")]
IntegralTypeTooWide, IntegralTypeTooWide,
#[error("Dummy message.")]
TriedToDerefNonPointer,
} }
pub type Result<T> = core::result::Result<T, Error>; pub type Result<T> = core::result::Result<T, Error>;
// TODO: add a string-table which stores strings and maybe other bytes and
// returns a range for identifiers, constants, etc. where bytes are stored
// flatly, and next to each other.
#[derive(Debug)] #[derive(Debug)]
pub struct Tree { pub struct Tree {
nodes: Vec<Tag>, nodes: Vec<Tag>,
global_decls: Vec<Node>, pub global_decls: Vec<Node>,
} }
fn write_indented_inner<W: core::fmt::Write>( pub fn write_indented_inner<W: core::fmt::Write>(
dst: &mut W, dst: &mut W,
indent: u32, indent: u32,
nl: bool, nl: bool,
@ -47,15 +52,17 @@ fn write_indented_inner<W: core::fmt::Write>(
Ok(()) Ok(())
} }
#[macro_export]
macro_rules! write_indented { macro_rules! write_indented {
($indent:expr, $w:expr, $($arg:tt)*) => { ($indent:expr, $w:expr, $($arg:tt)*) => {
write_indented_inner($w, $indent, false, format_args!($($arg)*)) $crate::parser::write_indented_inner($w, $indent, false, format_args!($($arg)*))
}; };
} }
#[macro_export]
macro_rules! writeln_indented { macro_rules! writeln_indented {
($indent:expr, $w:expr, $($arg:tt)*) => { ($indent:expr, $w:expr, $($arg:tt)*) => {
write_indented_inner($w, $indent, true, format_args!($($arg)*)) $crate::parser::write_indented_inner($w, $indent, true, format_args!($($arg)*))
}; };
} }
@ -73,18 +80,16 @@ impl Tree {
node node
} }
fn set_node(&mut self, node: Node, tag: Tag) -> Option<()> { fn set_node(&mut self, node: Node, tag: Tag) {
*self.get_node_mut(node)? = tag; *self.get_node_mut(node) = tag;
Some(())
} }
fn get_node_mut(&mut self, node: Node) -> Option<&mut Tag> { fn get_node_mut(&mut self, node: Node) -> &mut Tag {
self.nodes.get_mut(node.get() as usize) self.nodes.get_mut(node.get() as usize).unwrap()
} }
fn get_node(&self, node: Node) -> Option<&Tag> { pub fn get_node(&self, node: Node) -> &Tag {
self.nodes.get(node.get() as usize) self.nodes.get(node.get() as usize).unwrap()
} }
fn push_tag(&mut self, tag: Tag) -> Node { fn push_tag(&mut self, tag: Tag) -> Node {
@ -301,10 +306,10 @@ impl Tree {
explicit_type, explicit_type,
assignment, assignment,
}, },
) );
.unwrap();
Ok(node) // return assignment if it exists, to make rendering and visiting easier
Ok(assignment.unwrap_or(node))
} }
pub fn parse_global_decl(&mut self, tokens: &mut TokenIterator) -> Result<Node> { pub fn parse_global_decl(&mut self, tokens: &mut TokenIterator) -> Result<Node> {
@ -726,7 +731,7 @@ impl Tree {
self.parse_program(&mut tokens) self.parse_program(&mut tokens)
} }
fn get_ident_str(&self, node: Node) -> Option<&str> { pub fn get_ident_str(&self, node: Node) -> Option<&str> {
match &self.nodes[node.get() as usize] { match &self.nodes[node.get() as usize] {
Tag::Ident { name } => Some(name.as_str()), Tag::Ident { name } => Some(name.as_str()),
_ => None, _ => None,
@ -734,7 +739,7 @@ impl Tree {
} }
fn get_typename_str(&self, node: Node) -> Option<String> { fn get_typename_str(&self, node: Node) -> Option<String> {
match self.get_node(node)? { match self.get_node(node) {
Tag::IntegralType(i) => Some(i.to_string()), Tag::IntegralType(i) => Some(i.to_string()),
Tag::Ident { name } => Some(name.clone()), Tag::Ident { name } => Some(name.clone()),
Tag::Pointer { pointee } => self.get_typename_str(*pointee), Tag::Pointer { pointee } => self.get_typename_str(*pointee),
@ -769,7 +774,7 @@ impl Tree {
writeln!(writer, "}}") writeln!(writer, "}}")
} }
Tag::ParameterList { parameters } => { Tag::ParameterList { parameters } => {
writeln_indented!(indent, writer, "%{} = ParameterList = [", node.get())?; writeln_indented!(indent, writer, "%{} = ParameterList [", node.get())?;
for param in parameters { for param in parameters {
self.render_node(writer, *param, indent + 1)?; self.render_node(writer, *param, indent + 1)?;
} }
@ -864,7 +869,7 @@ impl Tree {
let_or_var, let_or_var,
name, name,
explicit_type, explicit_type,
assignment, ..
} => { } => {
self.render_node(writer, *name, indent)?; self.render_node(writer, *name, indent)?;
explicit_type.map(|ty| self.render_node(writer, ty, indent)); explicit_type.map(|ty| self.render_node(writer, ty, indent));
@ -887,9 +892,6 @@ impl Tree {
write!(writer, ", ty: {}", self.get_typename_str(*ty).unwrap())?; write!(writer, ", ty: {}", self.get_typename_str(*ty).unwrap())?;
} }
writeln!(writer, ");")?; writeln!(writer, ");")?;
if let Some(assignment) = assignment {
self.render_node(writer, *assignment, indent)?;
}
Ok(()) Ok(())
} }
Tag::CallExpr { lhs, rhs } => todo!(), Tag::CallExpr { lhs, rhs } => todo!(),
@ -1145,6 +1147,7 @@ impl Tree {
) )
} }
Tag::Assign { lhs, rhs } => { Tag::Assign { lhs, rhs } => {
self.render_node(writer, *lhs, indent)?;
self.render_node(writer, *rhs, indent)?; self.render_node(writer, *rhs, indent)?;
writeln_indented!( writeln_indented!(
indent, indent,

608
src/triples.rs Normal file
View file

@ -0,0 +1,608 @@
#![allow(dead_code)]
use std::{collections::HashMap, marker::PhantomData, num::NonZero};
use crate::{
ast::{FloatingType, IntegralType, Node as AstNode, PrimitiveType, Tag, Type},
parser::Tree,
writeln_indented,
};
struct SymbolRecord {
name: String,
decl: AstNode,
ty: Type,
}
struct SymbolTable<'a> {
tree: &'a Tree,
identifiers: Vec<SymbolRecord>,
parent: Option<Box<SymbolTable<'a>>>,
}
impl<'a> SymbolTable<'a> {
pub fn root(tree: &'a Tree) -> Self {
Self {
tree,
identifiers: Vec::new(),
parent: None,
}
}
fn insert_symbol_with_name(&mut self, ast_node: AstNode, name: String) {
let ty = self.type_of_node(ast_node);
self.identifiers.push(SymbolRecord {
name,
decl: ast_node,
ty,
});
}
pub fn insert_return_symbol(&mut self, ast_node: AstNode) {
self.insert_symbol_with_name(ast_node, "return".to_string())
}
pub fn insert_symbol(&mut self, ast_node: AstNode) {
let name = match self.tree.get_node(ast_node) {
Tag::VarDecl { name, .. } => self.tree.get_ident_str(*name).unwrap().to_string(),
Tag::Parameter { name, .. } => self.tree.get_ident_str(*name).unwrap().to_string(),
Tag::FunctionProto { name, .. } => self.tree.get_ident_str(*name).unwrap().to_string(),
_ => {
panic!("ast_node wasn't any kind of decl!");
}
};
self.insert_symbol_with_name(ast_node, name)
}
pub fn find_symbol_name(&self, name: &str) -> Option<&SymbolRecord> {
self.identifiers
.iter()
.find(|r| r.name.as_str() == name)
.or_else(|| {
self.parent
.as_ref()
.and_then(|parent| parent.find_symbol_name(name))
})
}
pub fn find_symbol(&self, ident_node: AstNode) -> Option<&SymbolRecord> {
self.identifiers
.iter()
.find(|r| Some(r.name.as_str()) == self.tree.get_ident_str(ident_node))
.or_else(|| {
self.parent
.as_ref()
.and_then(|parent| parent.find_symbol(ident_node))
})
}
pub fn into_child_in_place(&mut self) {
let mut parent = Self {
parent: None,
tree: self.tree,
identifiers: Vec::new(),
};
core::mem::swap(self, &mut parent);
self.parent = Some(Box::new(parent));
}
pub fn into_child(self) -> SymbolTable<'a> {
Self {
identifiers: Vec::new(),
tree: self.tree,
parent: Some(Box::new(self)),
}
}
pub fn parent_mut(&mut self) -> Option<&mut SymbolTable<'a>> {
self.parent.as_mut().map(|parent| parent.as_mut())
}
pub fn into_parent_in_place(&mut self) -> Option<SymbolTable<'a>> {
if let Some(child) = self.parent.take() {
let mut child = Box::into_inner(child);
core::mem::swap(self, &mut child);
Some(child)
} else {
None
}
}
pub fn into_parent(self) -> Option<SymbolTable<'a>> {
self.parent.map(|parent| Box::into_inner(parent))
}
fn type_of_node(&self, node: AstNode) -> crate::ast::Type {
match self.tree.get_node(node) {
Tag::Ident { name } => self
.find_symbol(node)
.map(|r| r.ty.clone())
.expect(&format!("identifier '{name}' not found in SymbolTable!")),
Tag::FunctionDecl { proto, .. } => self.type_of_node(*proto),
Tag::FunctionProto {
parameters,
return_type,
..
} => {
let return_type = self.type_of_node(*return_type);
let parameter_types = parameters
.map(|p| match self.tree.get_node(p) {
Tag::ParameterList { parameters } => parameters
.iter()
.map(|p| self.type_of_node(*p))
.collect::<Vec<_>>(),
_ => panic!("parameters is not a parameterlist!"),
})
.unwrap_or(Vec::new());
crate::ast::Type::Fn {
parameter_types,
return_type: Box::new(return_type),
}
}
Tag::Parameter { ty, .. } => self.type_of_node(*ty),
Tag::Pointer { pointee } => Type::Pointer {
constness: false,
pointee: Box::new(self.type_of_node(*pointee)),
},
Tag::IntegralType(t) => Type::Integer(*t),
Tag::PrimitiveType(t) => match t {
PrimitiveType::FloatingType(t) => Type::Floating(*t),
PrimitiveType::IntegralType(t) => self.type_of_node(*t),
PrimitiveType::Bool => Type::bool(),
PrimitiveType::Void => Type::void(),
},
Tag::IntegralConstant { ty, .. } => Type::Integer(*ty),
Tag::FloatingConstant { ty, .. } => Type::Floating(*ty),
Tag::Block { trailing_expr, .. } => trailing_expr
.map(|n| self.type_of_node(n))
.unwrap_or(Type::void()),
Tag::VarDecl {
explicit_type,
assignment, // this is a Tag::Assign
..
} => {
let lhs = explicit_type.map(|n| self.type_of_node(n));
let rhs = assignment.map(|n| match self.tree.get_node(n) {
Tag::Assign { rhs, .. } => self.type_of_node(*rhs),
_ => unreachable!(),
});
if lhs.as_ref().zip(rhs.as_ref()).map(|(l, r)| l != r) == Some(true) {
eprintln!("vardecl: incompatible types {lhs:?} and {rhs:?}.");
}
lhs.or(rhs)
.expect("Type could not be automatically deduced.")
}
Tag::CallExpr { lhs, .. } => self.type_of_node(*lhs),
Tag::ExplicitCast { typename, .. } => self.type_of_node(*typename),
Tag::Deref { lhs } => self.type_of_node(*lhs).remove_ptr().unwrap(),
Tag::Ref { lhs } => self.type_of_node(*lhs).into_ptr(),
Tag::Not { lhs } => self.type_of_node(*lhs),
Tag::Negate { lhs } => self.type_of_node(*lhs),
Tag::Or { lhs, .. } => self.type_of_node(*lhs),
Tag::And { lhs, .. } => self.type_of_node(*lhs),
Tag::BitOr { lhs, .. } => self.type_of_node(*lhs),
Tag::BitAnd { lhs, .. } => self.type_of_node(*lhs),
Tag::BitXOr { lhs, .. } => self.type_of_node(*lhs),
Tag::Shl { lhs, .. } => self.type_of_node(*lhs),
Tag::Shr { lhs, .. } => self.type_of_node(*lhs),
Tag::Add { lhs, .. } => self.type_of_node(*lhs),
Tag::Sub { lhs, .. } => self.type_of_node(*lhs),
Tag::Mul { lhs, .. } => self.type_of_node(*lhs),
Tag::Rem { lhs, .. } => self.type_of_node(*lhs),
Tag::Div { lhs, .. } => self.type_of_node(*lhs),
Tag::Eq { .. } => Type::bool(),
Tag::NEq { .. } => Type::bool(),
Tag::Lt { .. } => Type::bool(),
Tag::Gt { .. } => Type::bool(),
Tag::Le { .. } => Type::bool(),
Tag::Ge { .. } => Type::bool(),
_ => Type::void(),
}
}
}
type Node = u32;
enum Inst {
Label(String),
Constant(Value),
Parameter,
Add { lhs: Node, rhs: Node },
Sub { lhs: Node, rhs: Node },
Div { lhs: Node, rhs: Node },
Mul { lhs: Node, rhs: Node },
Rem { lhs: Node, rhs: Node },
BitAnd { lhs: Node, rhs: Node },
BitOr { lhs: Node, rhs: Node },
BitXOr { lhs: Node, rhs: Node },
Negate { lhs: Node },
ReturnValue { lhs: Node },
Return,
Alloc { size: u32, align: u32 },
AddressOf(Node),
Load { source: Node },
Store { dest: Node, source: Node },
}
enum Value {
Int { kind: IntegralType, bits: u64 },
Float { kind: FloatingType, bits: u64 },
}
impl core::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Value::Int { kind, bits } => {
write!(f, "{kind} {}", bits)
}
Value::Float { kind, bits } => {
write!(f, "{kind} {{{}}}", bits)
}
}
}
}
struct IRBuilder<'tree, 'ir> {
ir: &'ir mut IR,
st: SymbolTable<'tree>,
lookup: HashMap<AstNode, Node>,
}
impl<'tree, 'ir> IRBuilder<'tree, 'ir> {
fn new(ir: &'ir mut IR, st: SymbolTable<'tree>) -> Self {
Self {
ir,
st,
lookup: HashMap::new(),
}
}
fn visit(&mut self, node: AstNode) -> Node {
println!("visiting %{}", node.get());
match self.st.tree.get_node(node) {
Tag::FunctionDecl { proto, body } => {
self.visit(*proto);
self.st.into_child_in_place();
let value = self.visit(*body);
// TODO: return value of body expression
let node = if value != !0 {
self.type_check(self.st.find_symbol_name("return").unwrap().decl, *body);
self.ir.push(Inst::ReturnValue { lhs: value })
} else {
!0
};
self.st.into_parent_in_place();
node
}
Tag::FunctionProto {
parameters,
return_type,
name,
} => {
self.st.insert_symbol(node);
self.st.insert_return_symbol(*return_type);
parameters.map(|p| self.visit(p));
let label = self.ir.push(Inst::Label(
self.st.tree.get_ident_str(*name).unwrap().to_string(),
));
self.lookup.insert(node, label);
label
}
Tag::ParameterList { parameters } => {
for param in parameters {
self.visit(*param);
}
!0
}
Tag::Parameter { .. } => {
self.st.insert_symbol(node);
let param = self.ir.push(Inst::Parameter);
self.lookup.insert(node, param);
param
}
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 { .. } => {
let ty = self.st.type_of_node(node);
let alloca = self.ir.push(Inst::Alloc {
size: ty.size_of(),
align: ty.align_of(),
});
self.st.insert_symbol(node);
self.lookup.insert(node, alloca);
alloca
}
Tag::ReturnStmt { expr } => {
if let Some(expr) = expr {
let expr = self.visit(*expr);
self.ir.push(Inst::ReturnValue { lhs: expr })
} else {
self.ir.push(Inst::Return)
}
}
Tag::ExprStmt { expr } => self.visit(*expr),
Tag::Deref { lhs } => {
let lhs = self.visit(*lhs);
self.ir.push(Inst::Load { source: lhs })
}
Tag::IntegralConstant { bits, ty } => self.ir.push(Inst::Constant(Value::Int {
kind: *ty,
bits: *bits,
})),
Tag::FloatingConstant { bits, ty } => self.ir.push(Inst::Constant(Value::Float {
kind: *ty,
bits: *bits,
})),
Tag::Assign { lhs, rhs } => {
self.type_check(*lhs, *rhs);
let lhs = self.visit(*lhs);
let rhs = self.visit(*rhs);
self.ir.push(Inst::Store {
dest: lhs,
source: rhs,
})
}
Tag::Add { lhs, rhs } => {
let ty = self.type_check(*lhs, *rhs);
if !ty.can_add_sub() {
eprintln!("add is not available for type {ty:?}");
}
let lhs = self.visit(*lhs);
let rhs = self.visit(*rhs);
self.ir.push(Inst::Add { lhs, rhs })
}
Tag::Sub { lhs, rhs } => {
let ty = self.type_check(*lhs, *rhs);
if !ty.can_add_sub() {
eprintln!("sub is not available for type {ty:?}");
}
let lhs = self.visit(*lhs);
let rhs = self.visit(*rhs);
self.ir.push(Inst::Sub { lhs, rhs })
}
Tag::Mul { lhs, rhs } => {
let ty = self.type_check(*lhs, *rhs);
if !ty.can_mul_div_rem() {
eprintln!("mul is not available for type {ty:?}");
}
let lhs = self.visit(*lhs);
let rhs = self.visit(*rhs);
self.ir.push(Inst::Mul { lhs, rhs })
}
Tag::Div { lhs, rhs } => {
let ty = self.type_check(*lhs, *rhs);
if !ty.can_mul_div_rem() {
eprintln!("div is not available for type {ty:?}");
}
let lhs = self.visit(*lhs);
let rhs = self.visit(*rhs);
self.ir.push(Inst::Div { lhs, rhs })
}
Tag::Rem { lhs, rhs } => {
let ty = self.type_check(*lhs, *rhs);
if !ty.can_mul_div_rem() {
eprintln!("rem is not available for type {ty:?}");
}
let lhs = self.visit(*lhs);
let rhs = self.visit(*rhs);
self.ir.push(Inst::Rem { lhs, rhs })
}
Tag::BitAnd { lhs, rhs } => {
let ty = self.type_check(*lhs, *rhs);
if !ty.can_bitxor_and_or() {
eprintln!("bitand is not available for type {ty:?}");
}
let lhs = self.visit(*lhs);
let rhs = self.visit(*rhs);
self.ir.push(Inst::BitAnd { lhs, rhs })
}
Tag::BitOr { lhs, rhs } => {
let ty = self.type_check(*lhs, *rhs);
if !ty.can_bitxor_and_or() {
eprintln!("bitor is not available for type {ty:?}");
}
let lhs = self.visit(*lhs);
let rhs = self.visit(*rhs);
self.ir.push(Inst::BitOr { lhs, rhs })
}
Tag::BitXOr { lhs, rhs } => {
let ty = self.type_check(*lhs, *rhs);
if !ty.can_bitxor_and_or() {
eprintln!("bitxor is not available for type {ty:?}");
}
let lhs = self.visit(*lhs);
let rhs = self.visit(*rhs);
self.ir.push(Inst::BitXOr { lhs, rhs })
}
Tag::Negate { lhs } => {
let ty = self.st.type_of_node(*lhs);
if !ty.can_negate() {
eprintln!("negation is not available for type {ty:?}");
}
let lhs = self.visit(*lhs);
self.ir.push(Inst::Negate { lhs })
}
Tag::Ident { name } => {
let decl = self
.st
.find_symbol(node)
.expect(&format!("symbol '{name}' not found in SymbolMap!"))
.decl;
*self.lookup.get(&decl).unwrap()
}
Tag::Ref { lhs } => {
let lhs = self.visit(*lhs);
self.ir.push(Inst::AddressOf(lhs))
}
_ => {
dbg!(self.st.tree.get_node(node));
todo!()
}
}
}
fn type_check(&self, lhs: AstNode, rhs: AstNode) -> Type {
let t_lhs = self.st.type_of_node(lhs);
let t_rhs = self.st.type_of_node(rhs);
if t_lhs != t_rhs {
eprintln!("incompatible types {t_lhs:?} and {t_rhs:?}!");
}
t_lhs
}
}
struct IR {
nodes: Vec<Inst>,
}
impl IR {
pub fn new() -> Self {
Self { nodes: Vec::new() }
}
fn push(&mut self, inst: Inst) -> u32 {
let node = self.nodes.len() as u32;
self.nodes.push(inst);
node
}
pub fn build(&mut self, tree: &Tree, ast_node: crate::ast::Node) {
let st = SymbolTable::root(tree);
let mut builder = IRBuilder::new(self, st);
builder.visit(ast_node);
}
fn render_node<W: core::fmt::Write>(
&self,
w: &mut W,
node: Node,
indent: u32,
) -> core::fmt::Result {
match &self.nodes[node as usize] {
Inst::Label(label) => {
writeln_indented!(indent - 1, w, "%{} = {label}:", node)?;
}
Inst::Parameter => {
writeln_indented!(indent, w, "%{} = Param", node)?;
}
Inst::Constant(value) => {
writeln_indented!(indent, w, "%{} = {}", node, value)?;
}
Inst::Add { lhs, rhs } => {
writeln_indented!(indent, w, "%{} = %{} + %{}", node, lhs, rhs)?;
}
Inst::Sub { lhs, rhs } => {
writeln_indented!(indent, w, "%{} = %{} - %{}", node, lhs, rhs)?;
}
Inst::Mul { lhs, rhs } => {
writeln_indented!(indent, w, "%{} = %{} * %{}", node, lhs, rhs)?;
}
Inst::Div { lhs, rhs } => {
writeln_indented!(indent, w, "%{} = %{} / %{}", node, lhs, rhs)?;
}
Inst::Rem { lhs, rhs } => {
writeln_indented!(indent, w, "%{} = %{} % %{}", node, lhs, rhs)?;
}
Inst::BitAnd { lhs, rhs } => {
writeln_indented!(indent, w, "%{} = %{} & %{}", node, lhs, rhs)?;
}
Inst::BitOr { lhs, rhs } => {
writeln_indented!(indent, w, "%{} = %{} | %{}", node, lhs, rhs)?;
}
Inst::BitXOr { lhs, rhs } => {
writeln_indented!(indent, w, "%{} = %{} ^ %{}", node, lhs, rhs)?;
}
Inst::Negate { lhs } => {
writeln_indented!(indent, w, "%{} = !%{}", node, lhs)?;
}
Inst::ReturnValue { lhs } => {
writeln_indented!(indent, w, "%{} = return %{}", node, lhs)?;
}
Inst::Return => {
writeln_indented!(indent, w, "%{} = return", node)?;
}
Inst::Alloc { size, align } => {
writeln_indented!(indent, w, "%{} = alloca {size} (algin: {align})", node)?;
}
Inst::AddressOf(val) => {
writeln_indented!(indent, w, "%{} = addr %{val}", node)?;
}
Inst::Load { source } => {
writeln_indented!(indent, w, "%{} = load ptr %{source}", node)?;
}
Inst::Store { dest, source } => {
writeln_indented!(indent, w, "%{} = store ptr %{dest} from %{source}", node)?;
}
}
Ok(())
}
pub fn render<W: core::fmt::Write>(&self, w: &mut W) -> core::fmt::Result {
for node in 0..self.nodes.len() {
self.render_node(w, node as u32, 1)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::lexer::Tokenizer;
use super::*;
#[test]
fn ir() {
let src = "
fn main() -> u32 {
let a: u32 = 0 + 3;
let ptr_a = &a;
return *ptr_a * 2;
}
";
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!("{buf}");
let mut ir = IR::new();
ir.build(&tree, *tree.global_decls.first().unwrap());
let mut buf = String::new();
ir.render(&mut buf).unwrap();
println!("{buf}");
}
}