diff --git a/analysis/src/ir.rs b/analysis/src/ir.rs index 99ce179..8f683b1 100644 --- a/analysis/src/ir.rs +++ b/analysis/src/ir.rs @@ -122,6 +122,11 @@ pub enum StatementKind { rhs: Operand, result: Register, }, + UnaryOperation { + rhs: Operand, + kind: UnaryKind, + result: Register, + }, PtrOffset { result: Register, reg: Register, @@ -171,6 +176,13 @@ pub enum BinKind { BitXor, } +#[derive(Debug, Clone, Copy)] +pub enum UnaryKind { + Negate, + BitNot, + LogicalNot, +} + #[derive(Debug, Clone, Copy)] pub enum ConstValue { Void, diff --git a/analysis/src/ir/pretty.rs b/analysis/src/ir/pretty.rs index ef2da7e..c2e3e81 100644 --- a/analysis/src/ir/pretty.rs +++ b/analysis/src/ir/pretty.rs @@ -1,6 +1,6 @@ use std::fmt::{Display, Formatter, Result, Write}; -use super::{BbIdx, BinKind, Branch, ConstValue, Func, Ir, Operand, StatementKind}; +use super::{BbIdx, BinKind, Branch, ConstValue, Func, Ir, Operand, StatementKind, UnaryKind}; use crate::ir::Register; pub fn ir_to_string(ir: &Ir<'_>) -> String { @@ -125,6 +125,17 @@ impl PrettyPrinter { print_op(lhs), print_op(rhs) ), + StatementKind::UnaryOperation { rhs, kind, result } => writeln!( + self.out, + " {} = {} {}", + print_reg(result), + match kind { + UnaryKind::Negate => "negate", + UnaryKind::BitNot => "bitnot", + UnaryKind::LogicalNot => "logicalnot", + }, + print_op(rhs) + ), StatementKind::PtrOffset { result, reg, diff --git a/analysis/src/ir/validate.rs b/analysis/src/ir/validate.rs index 20325d7..64a3096 100644 --- a/analysis/src/ir/validate.rs +++ b/analysis/src/ir/validate.rs @@ -1,3 +1,5 @@ +use rustc_hash::FxHashSet; + use super::{Branch, Ir}; use crate::ir::BbIdx; @@ -5,7 +7,21 @@ pub fn validate(ir: &Ir<'_>) { for fun in ir.funcs.values() { for (i, bb) in fun.bbs.iter().enumerate() { if let Branch::Goto(BbIdx(u32::MAX)) = bb.term { - panic!("found dummy term in {} in {}", BbIdx::from_usize(i), fun.name) + panic!( + "found dummy term in {} in {}", + BbIdx::from_usize(i), + fun.name + ) + } + } + + let mut reg_names = FxHashSet::default(); + for reg in &fun.regs { + if let Some(name) = reg.name { + let is_new = reg_names.insert(name); + if !is_new { + panic!("register name {name} is used twice"); + } } } } diff --git a/analysis/src/lower.rs b/analysis/src/lower.rs index e1eb4c5..1638649 100644 --- a/analysis/src/lower.rs +++ b/analysis/src/lower.rs @@ -2,20 +2,14 @@ mod builder; use std::cell::{Cell, RefCell}; -use parser::{ - ast::{ - self, Atom, DeclAttr, Expr, ExprBinary, ExternalDecl, InitDecl, Stmt, TranslationUnit, - TypeSpecifier, - }, - Span, Symbol, -}; +use parser::{ast, Span, Symbol}; use rustc_hash::{FxHashMap, FxHashSet}; use self::builder::FuncBuilder; use crate::{ ir::{ self, BbIdx, BinKind, Branch, ConstValue, DefId, Func, Ir, Layout, Operand, Register, - TyLayout, + TyLayout, UnaryKind, }, ty::{Ty, TyKind}, Error, @@ -37,17 +31,17 @@ impl<'cx> LoweringCx<'cx> { self.next_def_id.set(DefId(def_id.0 + 1)); def_id } - fn lower_ty(&self, ty: &TypeSpecifier) -> Ty<'cx> { + fn lower_ty(&self, ty: &ast::TypeSpecifier) -> Ty<'cx> { let kind = match ty { - TypeSpecifier::Void => TyKind::Void, - TypeSpecifier::Char => TyKind::Char, - TypeSpecifier::SChar => TyKind::SChar, - TypeSpecifier::UChar => TyKind::UChar, - TypeSpecifier::Integer(int) => TyKind::Integer(*int), - TypeSpecifier::Float => TyKind::Float, - TypeSpecifier::Double => TyKind::Double, - TypeSpecifier::LongDouble => TyKind::LongDouble, - TypeSpecifier::Bool => TyKind::Bool, + ast::TypeSpecifier::Void => TyKind::Void, + ast::TypeSpecifier::Char => TyKind::Char, + ast::TypeSpecifier::SChar => TyKind::SChar, + ast::TypeSpecifier::UChar => TyKind::UChar, + ast::TypeSpecifier::Integer(int) => TyKind::Integer(*int), + ast::TypeSpecifier::Float => TyKind::Float, + ast::TypeSpecifier::Double => TyKind::Double, + ast::TypeSpecifier::LongDouble => TyKind::LongDouble, + ast::TypeSpecifier::Bool => TyKind::Bool, }; self.intern_ty(kind) } @@ -104,7 +98,7 @@ impl<'cx> LoweringCx<'cx> { pub fn lower_translation_unit<'cx>( arena: &'cx bumpalo::Bump, - ast: &TranslationUnit, + ast: &ast::TranslationUnit, ) -> Result, Error> { let lcx = LoweringCx { tys: RefCell::default(), @@ -119,8 +113,8 @@ pub fn lower_translation_unit<'cx>( for (decl, _) in ast { match decl { - ExternalDecl::Decl(_) => todo!("decl is unsupported"), - ExternalDecl::FunctionDef(def) => { + ast::ExternalDecl::Decl(_) => todo!("decl is unsupported"), + ast::ExternalDecl::FunctionDef(def) => { let decl = def.decl.unwrap_normal(); let body = &def.body; let ret_ty = lcx.lower_ty(&decl.decl_spec.ty); @@ -154,7 +148,7 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> { self.scopes.iter().rev().find_map(|s| s.get(&ident)) } - fn lower_block(&mut self, body: &[(Stmt, Span)]) -> Result<()> { + fn lower_block(&mut self, body: &[(ast::Stmt, Span)]) -> Result<()> { self.scopes.push(Default::default()); for (stmt, stmt_span) in body { self.lower_stmt(stmt, *stmt_span)?; @@ -195,16 +189,26 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> { Ok(()) } + fn expr_as_lvalue(&mut self, expr: &ast::Expr) -> Result { + let ast::Expr::Atom(ast::Atom::Ident((ident, ident_span))) = *expr else { + todo!("complex lvalues") + }; + let Some(var) = self.resolve_ident(ident) else { + return Err(Error::new(format!("cannot find variable {ident}"), ident_span)); + }; + Ok(var.ptr_to) + } + fn lower_stmt(&mut self, stmt: &ast::Stmt, stmt_span: Span) -> Result<()> { match stmt { - Stmt::Decl(decl) => { + ast::Stmt::Decl(decl) => { self.declare_local(decl, stmt_span)?; } - Stmt::Labeled { .. } => todo!("labels are not implemented"), - Stmt::Compound(block) => { + ast::Stmt::Labeled { .. } => todo!("labels are not implemented"), + ast::Stmt::Compound(block) => { self.lower_block(block)?; } - Stmt::If { + ast::Stmt::If { cond, then: then_body, otherwise, @@ -237,26 +241,20 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> { }; self.build.current_bb = cont; } - Stmt::Switch => todo!(), - Stmt::While { cond, body } => todo!(), - Stmt::For { - init_decl, - init_expr, - cond, - post, - body, - } => todo!(), - Stmt::Goto(_) => todo!(), - Stmt::Continue => todo!(), - Stmt::Break => todo!(), - Stmt::Return(expr) => { + ast::Stmt::Switch => todo!(), + ast::Stmt::While { .. } => todo!(), + ast::Stmt::For { .. } => todo!(), + ast::Stmt::Goto(_) => todo!(), + ast::Stmt::Continue => todo!(), + ast::Stmt::Break => todo!(), + ast::Stmt::Return(expr) => { let ret = match expr { Some(expr) => self.lower_expr(&expr.0, expr.1)?, None => Operand::Const(ConstValue::Void), }; self.build.cur_bb_mut().term = Branch::Ret(ret); } - Stmt::Expr(ast::Expr::Binary(ast::ExprBinary { + ast::Stmt::Expr(ast::Expr::Binary(ast::ExprBinary { op: ast::BinaryOp::Assign(assign), lhs, rhs, @@ -265,7 +263,7 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> { todo!("assign operation"); } let rhs = self.lower_expr(&rhs.0, rhs.1)?; - let (Expr::Atom(ast::Atom::Ident((ident, ident_span))), _) = **lhs else { + let (ast::Expr::Atom(ast::Atom::Ident((ident, ident_span))), _) = **lhs else { todo!("complex assignments") }; let Some(var) = self.resolve_ident(ident) else { @@ -273,7 +271,7 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> { }; self.build.store(var.ptr_to, rhs, var.tyl.layout, stmt_span); } - Stmt::Expr(expr) => { + ast::Stmt::Expr(expr) => { self.lower_expr(expr, stmt_span)?; } } @@ -283,29 +281,72 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> { 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((ident, ident_span))) => { + ast::Expr::Atom(ast::Atom::Char(c)) => Ok(Operand::Const(ConstValue::Int((*c).into()))), + ast::Expr::Atom(ast::Atom::Int(i)) => Ok(Operand::Const(ConstValue::Int(*i as _))), + ast::Expr::Atom(ast::Atom::Float(_)) => todo!("no floats"), + ast::Expr::Atom(ast::Atom::Ident((ident, ident_span))) => { let Some(var) = self.resolve_ident(*ident) else { return Err(Error::new(format!("cannot find variable {ident}"), *ident_span)); }; let op = self.build.load(var.tyl, var.ptr_to, span); Ok(Operand::Reg(op)) } - ast::Expr::Atom(Atom::String(_)) => todo!("no string literals"), + ast::Expr::Atom(ast::Atom::String(_)) => todo!("no string literals"), + ast::Expr::Unary(ast::ExprUnary { + op: op @ (ast::UnaryOp::Increment | ast::UnaryOp::Decrement), + rhs: rhs_expr, + }) => { + // First increment/decrement, then return the value. + let rhs = self.lower_expr(&rhs_expr.0, rhs_expr.1)?; + let lvalue = self.expr_as_lvalue(&rhs_expr.0)?; + let bin_kind = if let ast::UnaryOp::Increment = op { + BinKind::Add + } else { + BinKind::Sub + }; + let lhs = self.build.load( + self.lcx.layout_of(self.lcx.intern_ty(TyKind::Void)), + lvalue, + span, + ); + let result = self.build.binary( + bin_kind, + Operand::Reg(lhs), + rhs, + span, + self.lcx.layout_of(self.lcx.intern_ty(TyKind::Void)), + ); + self.build.store( + lhs, + rhs, + self.lcx.layout_of(self.lcx.intern_ty(TyKind::Void)).layout, + span, + ); + + Ok(Operand::Reg(result)) + } ast::Expr::Unary(unary) => { - let _rhs = self.lower_expr(&unary.rhs.0, unary.rhs.1)?; - match unary.op { + let rhs = self.lower_expr(&unary.rhs.0, unary.rhs.1)?; + let kind = match unary.op { + ast::UnaryOp::Increment => unreachable!("handled prefix increment above"), + ast::UnaryOp::Decrement => unreachable!("handled prefix increment above"), ast::UnaryOp::AddrOf => todo!("addr of"), ast::UnaryOp::Deref => todo!("deref?"), ast::UnaryOp::Plus => todo!("unary plus lol"), - ast::UnaryOp::Minus => todo!("unary minus!"), - ast::UnaryOp::Tilde => todo!("tilde"), - ast::UnaryOp::Bang => todo!("bang bang bang"), - } + ast::UnaryOp::Minus => UnaryKind::Negate, + ast::UnaryOp::Tilde => UnaryKind::BitNot, + ast::UnaryOp::Bang => UnaryKind::LogicalNot, + }; + + let reg = self.build.unary( + kind, + rhs, + span, + self.lcx.layout_of(self.lcx.intern_ty(TyKind::Void)), + ); + Ok(Operand::Reg(reg)) } - ast::Expr::Binary(ExprBinary { + ast::Expr::Binary(ast::ExprBinary { lhs, rhs, op: ast::BinaryOp::Assign(assign), @@ -314,13 +355,14 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> { todo!("assign operation"); } let rhs = self.lower_expr(&rhs.0, rhs.1)?; - let (Expr::Atom(ast::Atom::Ident((ident, ident_span))), _) = **lhs else { - todo!("complex assignments") - }; - let Some(var) = self.resolve_ident(ident) else { - return Err(Error::new(format!("cannot find variable {ident}"), ident_span)); - }; - self.build.store(var.ptr_to, rhs, var.tyl.layout, span); + + let ptr_to = self.expr_as_lvalue(&lhs.0)?; + self.build.store( + ptr_to, + rhs, + self.lcx.layout_of(self.lcx.intern_ty(TyKind::Void)).layout, + span, + ); Ok(rhs) } ast::Expr::Binary(binary) => { @@ -363,7 +405,7 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> { Ok(Operand::Reg(reg)) } - Expr::Postfix(postfix) => { + ast::Expr::Postfix(postfix) => { let lhs = self.lower_expr(&postfix.lhs.0, postfix.lhs.1)?; match &postfix.op { ast::PostfixOp::Call(args) => { @@ -397,13 +439,13 @@ struct VariableInfo<'cx> { def_span: Span, tyl: TyLayout<'cx>, ptr_to: Register, - decl_attr: DeclAttr, + decl_attr: ast::DeclAttr, } fn lower_func<'cx>( // may be used later lcx: &LoweringCx<'cx>, - body: &[(Stmt, Span)], + body: &[(ast::Stmt, Span)], def_span: Span, name: Symbol, ret_ty: Ty<'cx>, diff --git a/analysis/src/lower/builder.rs b/analysis/src/lower/builder.rs index b379a3b..77fa9ea 100644 --- a/analysis/src/lower/builder.rs +++ b/analysis/src/lower/builder.rs @@ -4,7 +4,7 @@ use super::LoweringCx; use crate::{ ir::{ self, BasicBlock, BbIdx, BinKind, Branch, ConstValue, Func, Layout, Operand, Register, - RegisterData, Statement, StatementKind, TyLayout, + RegisterData, Statement, StatementKind, TyLayout, UnaryKind, }, ty::{Ty, TyKind}, }; @@ -85,6 +85,25 @@ impl<'a, 'cx> FuncBuilder<'a, 'cx> { reg } + pub fn unary( + &mut self, + kind: UnaryKind, + rhs: Operand, + span: Span, + result_tyl: TyLayout<'cx>, + ) -> Register { + let reg = self.new_reg(None, result_tyl); + let stmt = StatementKind::UnaryOperation { + kind, + rhs, + result: reg, + }; + self.cur_bb_mut() + .statements + .push(Statement { span, kind: stmt }); + reg + } + pub fn load(&mut self, tyl: TyLayout<'cx>, ptr_reg: Register, span: Span) -> Register { let reg = self.new_reg(None, tyl.clone()); let stmt = StatementKind::Load { diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 740100e..cd9bc88 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -23,6 +23,8 @@ pub enum Atom { #[derive(Debug, DebugPls)] pub enum UnaryOp { + Increment, + Decrement, AddrOf, Deref, Plus, diff --git a/parser/src/pretty.rs b/parser/src/pretty.rs index deb87f5..62cc6a2 100644 --- a/parser/src/pretty.rs +++ b/parser/src/pretty.rs @@ -376,6 +376,8 @@ impl PrettyPrinter { fn unary(&mut self, unary: &ExprUnary) -> Result { self.string(match unary.op { + UnaryOp::Increment => "++", + UnaryOp::Decrement => "--", UnaryOp::AddrOf => "&", UnaryOp::Deref => "*", UnaryOp::Plus => "+",