diff --git a/analysis/src/ir/pretty.rs b/analysis/src/ir/pretty.rs new file mode 100644 index 0000000..0512af0 --- /dev/null +++ b/analysis/src/ir/pretty.rs @@ -0,0 +1,163 @@ +use std::fmt::{Display, Formatter, Result, Write}; + +use super::{BinKind, Branch, ConstValue, Func, Ir, Operand, StatementKind}; +use crate::ir::Register; + +pub fn ir_to_string(ir: &Ir) -> String { + let mut buf = String::new(); + PrettyPrinter { out: &mut buf }.ir(ir).unwrap(); + buf +} + +pub fn func_to_string(func: &Func) -> String { + let mut buf = String::new(); + PrettyPrinter { out: &mut buf }.func(func).unwrap(); + buf +} + +pub struct PrettyPrinter { + out: W, +} + +impl PrettyPrinter { + pub fn ir(&mut self, ir: &Ir) -> Result { + for (_, func) in &ir.funcs { + self.func(func)?; + } + Ok(()) + } + + pub fn func(&mut self, func: &Func) -> Result { + writeln!(self.out, "def {}() {{", func.name)?; + + let print_reg = |reg: Register| { + display_fn(move |f| match func.regs[reg.0 as usize].name { + None => write!(f, "%{}", reg.0), + Some(name) => write!(f, "%{name}"), + }) + }; + + let print_op = |op: Operand| { + display_fn(move |f| match op { + Operand::Const(c) => Display::fmt(&c, f), + Operand::Reg(reg) => Display::fmt(&print_reg(reg), f), + }) + }; + + for (i, bb) in func.bbs.iter().enumerate() { + if i > 0 { + writeln!(self.out)?; + } + writeln!(self.out, " {i}:")?; + + for stmt in &bb.statements { + match stmt.kind { + StatementKind::Alloca { reg, size, align } => { + writeln!( + self.out, + " {} = alloca, size={}, align={}", + print_reg(reg), + print_op(size), + print_op(align) + ) + } + StatementKind::Store { + ptr_reg, + value, + size, + align, + } => writeln!( + self.out, + " store {}, {}, size={}, align={}", + print_reg(ptr_reg), + print_op(value), + print_op(size), + print_op(align) + ), + StatementKind::Load { + result, + ptr_reg, + size, + align, + } => writeln!( + self.out, + " {} = load {}, size={}, align={}", + print_reg(result), + print_reg(ptr_reg), + print_op(size), + print_op(align) + ), + StatementKind::BinOp { + kind, + lhs, + rhs, + result, + } => writeln!( + self.out, + " {} = {} {}, {}", + print_reg(result), + match kind { + BinKind::Add => "add", + BinKind::Sub => "sub", + BinKind::Mul => "mul", + BinKind::Div => "div", + BinKind::Mod => "mod", + BinKind::Eq => "eq", + BinKind::Neq => "neq", + BinKind::Gt => "gt", + BinKind::Geq => "geq", + BinKind::Lt => "gl", + BinKind::Leq => "leq", + }, + print_op(lhs), + print_op(rhs) + ), + StatementKind::PtrOffset { + result, + reg, + amount, + } => writeln!( + self.out, + " {} = ptroffset {}, {}", + print_reg(result), + print_reg(reg), + print_op(amount) + ), + }?; + } + + match bb.term { + Branch::Goto(bbn) => writeln!(self.out, " goto {}", bbn)?, + Branch::Switch { cond, yes, no } => { + writeln!(self.out, " switch {}, {yes}, {no}", print_op(cond))? + } + Branch::Ret(op) => writeln!(self.out, " ret {}", print_op(op))?, + } + } + + Ok(()) + } +} + +impl Display for ConstValue { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + match self { + ConstValue::Int(int) => <_ as Display>::fmt(int, f), + ConstValue::Void => f.write_str("void"), + } + } +} + +fn display_fn) -> Result>(f: F) -> impl Display { + struct DisplayFn { + f: F, + } + + impl) -> Result> Display for DisplayFn { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + (self.f)(f) + } + } + + DisplayFn { f } +} diff --git a/analysis/src/lib.rs b/analysis/src/lib.rs index 31f0a1b..1c890a4 100644 --- a/analysis/src/lib.rs +++ b/analysis/src/lib.rs @@ -14,7 +14,10 @@ pub struct Error { } impl Error { - pub fn new(msg: String, span: Span) -> Self { - Self { msg, span } + pub fn new(msg: impl Into, span: Span) -> Self { + Self { + msg: msg.into(), + span, + } } } diff --git a/analysis/src/lower.rs b/analysis/src/lower.rs index c736bf9..6dc3d44 100644 --- a/analysis/src/lower.rs +++ b/analysis/src/lower.rs @@ -1,18 +1,20 @@ use parser::{ - ast::{DeclAttr, ExternalDecl, Stmt, TranslationUnit, TypeSpecifier}, + ast::{self, Atom, DeclAttr, Expr, ExternalDecl, Stmt, TranslationUnit, TypeSpecifier}, Span, Symbol, }; use rustc_hash::FxHashMap; use crate::{ ir::{ - BasicBlock, Branch, ConstValue, Func, Ir, Layout, Operand, Register, RegisterData, - Statement, StatementKind, + self, BasicBlock, BinKind, Branch, ConstValue, Func, Ir, Layout, Operand, Register, + RegisterData, Statement, StatementKind, }, ty::Ty, Error, }; +type Result = std::result::Result; + struct LoweringCx {} pub fn lower_translation_unit(ast: &TranslationUnit) -> Result { @@ -25,7 +27,13 @@ pub fn lower_translation_unit(ast: &TranslationUnit) -> Result { let decl = def.decl.uwnrap_normal(); let body = &def.body; let ret_ty = lower_ty(&decl.decl_spec.ty); - lower_body(&mut lcx, body, decl.init_declarators[0].1, ret_ty)?; + lower_body( + &mut lcx, + body, + decl.init_declarators[0].1, + decl.init_declarators[0].0.declarator.decl.name().0, + ret_ty, + )?; } } } @@ -41,10 +49,18 @@ struct FnLoweringCtxt { } impl FnLoweringCtxt { + fn resolve_ident(&self, ident: Symbol) -> Option<&VariableInfo> { + self.scopes.iter().rev().find_map(|s| s.get(&ident)) + } + + fn new_reg(&mut self, name: Option) -> Register { + let reg = Register(self.ir.regs.len().try_into().unwrap()); + self.ir.regs.push(RegisterData { name }); + reg + } + fn alloca(&mut self, layout: &Layout, name: Option, span: Span) -> Register { - let bb = self.bb_mut(); - let reg = Register(bb.regs.len().try_into().unwrap()); - bb.regs.push(RegisterData { name }); + let reg = self.new_reg(name); let stmt = Statement { span, kind: StatementKind::Alloca { @@ -53,10 +69,62 @@ impl FnLoweringCtxt { align: Operand::Const(ConstValue::u64(layout.align)), }, }; - bb.statements.push(stmt); + self.bb_mut().statements.push(stmt); reg } + fn lower_expr(&mut self, expr: &ast::Expr, span: Span) -> Result { + match expr { + ast::Expr::Atom(Atom::Char(c)) => Ok(Operand::Const(ConstValue::Int((*c).into()))), + ast::Expr::Atom(Atom::Int(i)) => Ok(Operand::Const(ConstValue::Int(*i as _))), + ast::Expr::Atom(Atom::Float(_)) => todo!("no floats"), + ast::Expr::Atom(Atom::Ident(_)) => todo!("no idents"), + ast::Expr::Atom(Atom::String(_)) => todo!("no string literals"), + ast::Expr::Unary(_) => todo!("no unaries"), + ast::Expr::Binary(binary) => { + let lhs = self.lower_expr(&binary.lhs.0, binary.lhs.1)?; + let rhs = self.lower_expr(&binary.rhs.0, binary.rhs.1)?; + let kind = match binary.op { + ast::BinaryOp::Arith(ast::ArithOpKind::Mul) => BinKind::Mul, + ast::BinaryOp::Arith(ast::ArithOpKind::Div) => BinKind::Div, + ast::BinaryOp::Arith(ast::ArithOpKind::Mod) => BinKind::Mod, + ast::BinaryOp::Arith(ast::ArithOpKind::Add) => BinKind::Add, + ast::BinaryOp::Arith(ast::ArithOpKind::Sub) => BinKind::Sub, + ast::BinaryOp::Arith(ast::ArithOpKind::Shl) => todo!("shl"), + ast::BinaryOp::Arith(ast::ArithOpKind::Shr) => todo!("shr"), + ast::BinaryOp::Arith(ast::ArithOpKind::BitAnd) => todo!("&"), + ast::BinaryOp::Arith(ast::ArithOpKind::BitXor) => todo!("^"), + ast::BinaryOp::Arith(ast::ArithOpKind::BitOr) => todo!("|"), + ast::BinaryOp::LogicalAnd => todo!("no logical or"), + ast::BinaryOp::LogicalOr => todo!("no logical and"), + ast::BinaryOp::Comparison(ast::ComparisonKind::Lt) => BinKind::Lt, + ast::BinaryOp::Comparison(ast::ComparisonKind::Gt) => BinKind::Gt, + ast::BinaryOp::Comparison(ast::ComparisonKind::LtEq) => BinKind::Leq, + ast::BinaryOp::Comparison(ast::ComparisonKind::GtEq) => BinKind::Geq, + ast::BinaryOp::Comparison(ast::ComparisonKind::Eq) => BinKind::Eq, + ast::BinaryOp::Comparison(ast::ComparisonKind::Neq) => BinKind::Neq, + ast::BinaryOp::Comma => todo!("no comma"), + ast::BinaryOp::Index => todo!("no index"), + ast::BinaryOp::Assign(_) => todo!("no assign"), + }; + + let reg = self.new_reg(None); + let stmt = StatementKind::BinOp { + kind, + lhs, + rhs, + result: reg, + }; + self.bb_mut() + .statements + .push(Statement { span, kind: stmt }); + + Ok(Operand::Reg(reg)) + } + Expr::Postfix(_) => todo!(), + } + } + fn bb_mut(&mut self) -> &mut BasicBlock { &mut self.ir.bbs[self.current_bb as usize] } @@ -76,16 +144,18 @@ fn lower_body( _lcx: &mut LoweringCx, body: &[(Stmt, Span)], def_span: Span, + name: Symbol, ret_ty: Ty, ) -> Result { let mut cx: FnLoweringCtxt = FnLoweringCtxt { scopes: vec![FxHashMap::default()], ir: Func { + regs: Vec::new(), bbs: vec![BasicBlock { - regs: Vec::new(), statements: Vec::new(), term: Branch::Goto(0), }], + name, def_span, ret_ty, }, @@ -134,12 +204,44 @@ fn lower_body( Stmt::Continue => todo!(), Stmt::Break => todo!(), Stmt::Return(_) => todo!(), - Stmt::Expr(_) => todo!(), + Stmt::Expr(ast::Expr::Binary(ast::ExprBinary { + op: ast::BinaryOp::Assign(assign), + lhs, + rhs, + })) => { + if assign.is_some() { + todo!("assign operation"); + } + let rhs = cx.lower_expr(&rhs.0, rhs.1)?; + let (Expr::Atom(ast::Atom::Ident((ident, ident_span))), _) = **lhs else { + todo!("complex assignments") + }; + let Some(var) = cx.resolve_ident(ident) else { + return Err(Error::new(format!("cannot find variable {ident}"), ident_span)); + }; + let stmt = StatementKind::Store { + ptr_reg: var.ptr_to, + value: rhs, + size: Operand::const_u64(var.layout.size), + align: Operand::const_u64(var.layout.align), + }; + cx.bb_mut().statements.push(Statement { + span: *stmt_span, + kind: stmt, + }); + } + Stmt::Expr(expr) => { + cx.lower_expr(expr, *stmt_span)?; + } } } + cx.bb_mut().term = Branch::Ret(Operand::Const(ConstValue::Void)); + dbg!(&cx); + println!("{}", ir::func_to_string(&cx.ir)); + Ok(cx.ir) } diff --git a/parser/src/ast.rs b/parser/src/ast.rs index fbdb856..e736cc2 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -3,7 +3,8 @@ use std::fmt::Debug; use bitflags::bitflags; use dbg_pls::DebugPls; -use crate::{sym::Symbol, Spanned}; +use crate::sym::Symbol; +pub use crate::Spanned; pub type Ident = Spanned; @@ -14,7 +15,7 @@ pub type Ident = Spanned; #[derive(Debug, DebugPls)] pub enum Atom { Ident(Ident), - Int(i128), + Int(u128), Float(f64), String(String), Char(u8), diff --git a/parser/src/parser.rs b/parser/src/parser.rs index 37a4e2e..ffd513a 100644 --- a/parser/src/parser.rs +++ b/parser/src/parser.rs @@ -512,7 +512,6 @@ where // TODO: recover here let stmt = self.statement()?; - stmts.push(stmt); }; Ok((stmts, brace_span.extend(end_span))) diff --git a/parser/src/sym.rs b/parser/src/sym.rs index 99a91af..e827c16 100644 --- a/parser/src/sym.rs +++ b/parser/src/sym.rs @@ -1,4 +1,8 @@ -use std::{cell::RefCell, fmt::Debug, marker::PhantomData}; +use std::{ + cell::RefCell, + fmt::{Debug, Display}, + marker::PhantomData, +}; use dbg_pls::DebugPls; use lasso::Spur; @@ -37,3 +41,9 @@ impl DebugPls for Symbol { self.as_str(|s| f.debug_ident(s)) } } + +impl Display for Symbol { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + INTERNER.with(|i| f.write_str(i.borrow_mut().resolve(&self.spur))) + } +} diff --git a/parser/src/token.rs b/parser/src/token.rs index 8dbb010..751b5d3 100644 --- a/parser/src/token.rs +++ b/parser/src/token.rs @@ -71,7 +71,7 @@ pub enum Keyword { #[derive(Debug, Clone, Copy)] pub enum Constant { - Int(i128), + Int(u128), Float(f64), Char(u8), // adding enumerations here makes no sense.