more stuff

This commit is contained in:
nora 2023-05-23 15:40:07 +02:00
parent 77e18126e7
commit c84fdfaf3a
12 changed files with 270 additions and 38 deletions

View file

@ -32,19 +32,21 @@
//! ``` //! ```
mod pretty; mod pretty;
mod validate;
use std::fmt::{Debug, Display}; use std::fmt::{Debug, Display};
use parser::{Span, Symbol}; use parser::{Span, Symbol};
pub use pretty::{func_to_string, ir_to_string}; pub use pretty::{func_to_string, ir_to_string};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
pub use validate::validate;
use crate::ty::Ty; use crate::ty::Ty;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DefId(u32); pub struct DefId(pub u32);
#[derive(Debug, Clone)] #[derive(Debug, Clone, Copy)]
pub struct TyLayout<'cx> { pub struct TyLayout<'cx> {
pub ty: Ty<'cx>, pub ty: Ty<'cx>,
pub layout: &'cx Layout, pub layout: &'cx Layout,
@ -160,6 +162,11 @@ pub enum BinKind {
Geq, Geq,
Lt, Lt,
Leq, Leq,
Shl,
Shr,
BitAnd,
BitOr,
BitXor,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -212,3 +219,9 @@ impl Operand {
Self::Const(ConstValue::u64(int)) Self::Const(ConstValue::u64(int))
} }
} }
impl Branch {
pub fn dummy() -> Self {
Branch::Goto(BbIdx(u32::MAX))
}
}

View file

@ -1,6 +1,6 @@
use std::fmt::{Display, Formatter, Result, Write}; use std::fmt::{Display, Formatter, Result, Write};
use super::{BinKind, Branch, ConstValue, Func, Ir, Operand, StatementKind, BbIdx}; use super::{BbIdx, BinKind, Branch, ConstValue, Func, Ir, Operand, StatementKind};
use crate::ir::Register; use crate::ir::Register;
pub fn ir_to_string(ir: &Ir<'_>) -> String { pub fn ir_to_string(ir: &Ir<'_>) -> String {
@ -21,7 +21,7 @@ pub struct PrettyPrinter<W> {
impl<W: Write> PrettyPrinter<W> { impl<W: Write> PrettyPrinter<W> {
pub fn ir(&mut self, ir: &Ir<'_>) -> Result { pub fn ir(&mut self, ir: &Ir<'_>) -> Result {
for (_, func) in &ir.funcs { for func in ir.funcs.values() {
self.func(func)?; self.func(func)?;
} }
Ok(()) Ok(())
@ -108,6 +108,11 @@ impl<W: Write> PrettyPrinter<W> {
BinKind::Geq => "geq", BinKind::Geq => "geq",
BinKind::Lt => "gl", BinKind::Lt => "gl",
BinKind::Leq => "leq", BinKind::Leq => "leq",
BinKind::Shl => "shl",
BinKind::Shr => "shr",
BinKind::BitAnd => "bitand",
BinKind::BitOr => "bitor",
BinKind::BitXor => "bitxor",
}, },
print_op(lhs), print_op(lhs),
print_op(rhs) print_op(rhs)

View file

@ -0,0 +1,12 @@
use super::{Branch, Ir};
use crate::ir::BbIdx;
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)
}
}
}
}

View file

@ -8,9 +8,17 @@ mod ty;
pub use lower::lower_translation_unit; pub use lower::lower_translation_unit;
use parser::Span; use parser::Span;
#[derive(Debug)]
pub struct Error { pub struct Error {
msg: String, msg: String,
span: Span, span: Span,
notes: Vec<Note>,
}
#[derive(Debug)]
struct Note {
msg: String,
span: Option<Span>,
} }
impl Error { impl Error {
@ -18,6 +26,15 @@ impl Error {
Self { Self {
msg: msg.into(), msg: msg.into(),
span, span,
notes: Vec::new(),
} }
} }
pub fn note_spanned(mut self, msg: impl Into<String>, span: Span) -> Self {
self.notes.push(Note {
msg: msg.into(),
span: Some(span),
});
self
}
} }

View file

@ -1,16 +1,21 @@
mod builder; mod builder;
use std::cell::RefCell; use std::cell::{Cell, RefCell};
use parser::{ use parser::{
ast::{self, Atom, DeclAttr, Expr, ExternalDecl, Stmt, TranslationUnit, TypeSpecifier}, ast::{
self, Atom, DeclAttr, Expr, ExprBinary, ExternalDecl, Stmt, TranslationUnit, TypeSpecifier,
},
Span, Symbol, Span, Symbol,
}; };
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use self::builder::FuncBuilder; use self::builder::FuncBuilder;
use crate::{ use crate::{
ir::{BinKind, Branch, ConstValue, Func, Ir, Layout, Operand, Register, TyLayout}, ir::{
self, BbIdx, BinKind, Branch, ConstValue, DefId, Func, Ir, Layout, Operand, Register,
TyLayout,
},
ty::{Ty, TyKind}, ty::{Ty, TyKind},
Error, Error,
}; };
@ -22,9 +27,15 @@ struct LoweringCx<'cx> {
tys: RefCell<FxHashSet<&'cx TyKind<'cx>>>, tys: RefCell<FxHashSet<&'cx TyKind<'cx>>>,
layouts: RefCell<FxHashSet<&'cx Layout>>, layouts: RefCell<FxHashSet<&'cx Layout>>,
arena: &'cx bumpalo::Bump, arena: &'cx bumpalo::Bump,
next_def_id: Cell<DefId>,
} }
impl<'cx> LoweringCx<'cx> { impl<'cx> LoweringCx<'cx> {
fn next_def_id(&self) -> DefId {
let def_id = self.next_def_id.get();
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: &TypeSpecifier) -> Ty<'cx> {
let kind = match ty { let kind = match ty {
TypeSpecifier::Void => TyKind::Void, TypeSpecifier::Void => TyKind::Void,
@ -94,10 +105,15 @@ pub fn lower_translation_unit<'cx>(
arena: &'cx bumpalo::Bump, arena: &'cx bumpalo::Bump,
ast: &TranslationUnit, ast: &TranslationUnit,
) -> Result<Ir<'cx>, Error> { ) -> Result<Ir<'cx>, Error> {
let mut lcx = LoweringCx { let lcx = LoweringCx {
tys: RefCell::default(), tys: RefCell::default(),
layouts: RefCell::default(), layouts: RefCell::default(),
arena, arena,
next_def_id: Cell::new(DefId(0)),
};
let mut ir = Ir {
funcs: FxHashMap::default(),
}; };
for (decl, _) in ast { for (decl, _) in ast {
@ -107,18 +123,21 @@ pub fn lower_translation_unit<'cx>(
let decl = def.decl.uwnrap_normal(); let decl = def.decl.uwnrap_normal();
let body = &def.body; let body = &def.body;
let ret_ty = lcx.lower_ty(&decl.decl_spec.ty); let ret_ty = lcx.lower_ty(&decl.decl_spec.ty);
lower_body( let func = lower_body(
&mut lcx, &lcx,
body, body,
decl.init_declarators[0].1, decl.init_declarators[0].1,
decl.init_declarators[0].0.declarator.decl.name().0, decl.init_declarators[0].0.declarator.decl.name().0,
ret_ty, ret_ty,
)?; )?;
ir.funcs.insert(lcx.next_def_id(), func);
} }
} }
} }
todo!("building is not really") ir::validate(&ir);
Ok(ir)
} }
#[derive(Debug)] #[derive(Debug)]
@ -133,10 +152,12 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
self.scopes.iter().rev().find_map(|s| s.get(&ident)) self.scopes.iter().rev().find_map(|s| s.get(&ident))
} }
fn lower_body(&mut self, body: &[(Stmt, Span)]) -> Result<()> { fn lower_block(&mut self, body: &[(Stmt, Span)]) -> Result<()> {
self.scopes.push(Default::default());
for (stmt, stmt_span) in body { for (stmt, stmt_span) in body {
self.lower_stmt(stmt, *stmt_span)?; self.lower_stmt(stmt, *stmt_span)?;
} }
self.scopes.pop();
Ok(()) Ok(())
} }
@ -148,9 +169,9 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
let decl_attr = decl.decl_spec.attrs; let decl_attr = decl.decl_spec.attrs;
for (var, def_span) in &decl.init_declarators { for (var, def_span) in &decl.init_declarators {
let tyl = self.lcx.layout_of(ty.clone()); let tyl = self.lcx.layout_of(ty);
let (name, _) = var.declarator.decl.name(); let (name, name_span) = var.declarator.decl.name();
let ptr_to = self.build.alloca(&tyl.layout, Some(name), stmt_span); let ptr_to = self.build.alloca(tyl.layout, Some(name), stmt_span);
let variable_info = VariableInfo { let variable_info = VariableInfo {
def_span: *def_span, def_span: *def_span,
@ -158,7 +179,14 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
decl_attr, decl_attr,
tyl: tyl.clone(), tyl: tyl.clone(),
}; };
self.scopes.last_mut().unwrap().insert(name, variable_info); let predeclared = self.scopes.last_mut().unwrap().insert(name, variable_info);
if let Some(predeclared) = predeclared {
return Err(Error::new(
format!("variable {name} has already been declared"),
name_span,
)
.note_spanned("already declared here", predeclared.def_span));
}
if let Some((init, init_span)) = &var.init { if let Some((init, init_span)) = &var.init {
let init = self.lower_expr(init, *init_span)?; let init = self.lower_expr(init, *init_span)?;
self.build.store(ptr_to, init, tyl.layout, *init_span); self.build.store(ptr_to, init, tyl.layout, *init_span);
@ -166,7 +194,9 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
} }
} }
Stmt::Labeled { .. } => todo!("labels are not implemented"), Stmt::Labeled { .. } => todo!("labels are not implemented"),
Stmt::Compound(_) => todo!("blocks are not implemented"), Stmt::Compound(block) => {
self.lower_block(block)?;
}
Stmt::If { Stmt::If {
cond, cond,
then: then_body, then: then_body,
@ -181,13 +211,13 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
let cont = self.build.new_block(); let cont = self.build.new_block();
self.build.current_bb = then; self.build.current_bb = then;
self.lower_body(&then_body)?; self.lower_block(then_body)?;
self.build.cur_bb_mut().term = Branch::Goto(cont); self.build.cur_bb_mut().term = Branch::Goto(cont);
let false_branch = match els { let false_branch = match els {
Some((otherwise, els)) => { Some((otherwise, els)) => {
self.build.current_bb = els; self.build.current_bb = els;
self.lower_body(&otherwise)?; self.lower_block(otherwise)?;
self.build.cur_bb_mut().term = Branch::Goto(cont); self.build.cur_bb_mut().term = Branch::Goto(cont);
els els
} }
@ -212,7 +242,13 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
Stmt::Goto(_) => todo!(), Stmt::Goto(_) => todo!(),
Stmt::Continue => todo!(), Stmt::Continue => todo!(),
Stmt::Break => todo!(), Stmt::Break => todo!(),
Stmt::Return(_) => todo!(), 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 { Stmt::Expr(ast::Expr::Binary(ast::ExprBinary {
op: ast::BinaryOp::Assign(assign), op: ast::BinaryOp::Assign(assign),
lhs, lhs,
@ -243,9 +279,43 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
ast::Expr::Atom(Atom::Char(c)) => Ok(Operand::Const(ConstValue::Int((*c).into()))), 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::Int(i)) => Ok(Operand::Const(ConstValue::Int(*i as _))),
ast::Expr::Atom(Atom::Float(_)) => todo!("no floats"), ast::Expr::Atom(Atom::Float(_)) => todo!("no floats"),
ast::Expr::Atom(Atom::Ident(_)) => todo!("no idents"), ast::Expr::Atom(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(Atom::String(_)) => todo!("no string literals"),
ast::Expr::Unary(_) => todo!("no unaries"), ast::Expr::Unary(unary) => {
let _rhs = self.lower_expr(&unary.rhs.0, unary.rhs.1)?;
match unary.op {
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::Expr::Binary(ExprBinary {
lhs,
rhs,
op: ast::BinaryOp::Assign(assign),
}) => {
if assign.is_some() {
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);
Ok(rhs)
}
ast::Expr::Binary(binary) => { ast::Expr::Binary(binary) => {
let lhs = self.lower_expr(&binary.lhs.0, binary.lhs.1)?; let lhs = self.lower_expr(&binary.lhs.0, binary.lhs.1)?;
let rhs = self.lower_expr(&binary.rhs.0, binary.rhs.1)?; let rhs = self.lower_expr(&binary.rhs.0, binary.rhs.1)?;
@ -255,11 +325,11 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
ast::BinaryOp::Arith(ast::ArithOpKind::Mod) => BinKind::Mod, ast::BinaryOp::Arith(ast::ArithOpKind::Mod) => BinKind::Mod,
ast::BinaryOp::Arith(ast::ArithOpKind::Add) => BinKind::Add, ast::BinaryOp::Arith(ast::ArithOpKind::Add) => BinKind::Add,
ast::BinaryOp::Arith(ast::ArithOpKind::Sub) => BinKind::Sub, ast::BinaryOp::Arith(ast::ArithOpKind::Sub) => BinKind::Sub,
ast::BinaryOp::Arith(ast::ArithOpKind::Shl) => todo!("shl"), ast::BinaryOp::Arith(ast::ArithOpKind::Shl) => BinKind::Shl,
ast::BinaryOp::Arith(ast::ArithOpKind::Shr) => todo!("shr"), ast::BinaryOp::Arith(ast::ArithOpKind::Shr) => BinKind::Shr,
ast::BinaryOp::Arith(ast::ArithOpKind::BitAnd) => todo!("&"), ast::BinaryOp::Arith(ast::ArithOpKind::BitAnd) => BinKind::BitAnd,
ast::BinaryOp::Arith(ast::ArithOpKind::BitXor) => todo!("^"), ast::BinaryOp::Arith(ast::ArithOpKind::BitXor) => BinKind::BitXor,
ast::BinaryOp::Arith(ast::ArithOpKind::BitOr) => todo!("|"), ast::BinaryOp::Arith(ast::ArithOpKind::BitOr) => BinKind::BitOr,
ast::BinaryOp::LogicalAnd => todo!("no logical or"), ast::BinaryOp::LogicalAnd => todo!("no logical or"),
ast::BinaryOp::LogicalOr => todo!("no logical and"), ast::BinaryOp::LogicalOr => todo!("no logical and"),
ast::BinaryOp::Comparison(ast::ComparisonKind::Lt) => BinKind::Lt, ast::BinaryOp::Comparison(ast::ComparisonKind::Lt) => BinKind::Lt,
@ -268,9 +338,12 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
ast::BinaryOp::Comparison(ast::ComparisonKind::GtEq) => BinKind::Geq, ast::BinaryOp::Comparison(ast::ComparisonKind::GtEq) => BinKind::Geq,
ast::BinaryOp::Comparison(ast::ComparisonKind::Eq) => BinKind::Eq, ast::BinaryOp::Comparison(ast::ComparisonKind::Eq) => BinKind::Eq,
ast::BinaryOp::Comparison(ast::ComparisonKind::Neq) => BinKind::Neq, ast::BinaryOp::Comparison(ast::ComparisonKind::Neq) => BinKind::Neq,
ast::BinaryOp::Comma => todo!("no comma"), ast::BinaryOp::Comma => {
// Discard the lhs, evaluate to the rhs.
return Ok(rhs);
}
ast::BinaryOp::Index => todo!("no index"), ast::BinaryOp::Index => todo!("no index"),
ast::BinaryOp::Assign(_) => todo!("no assign"), ast::BinaryOp::Assign(_) => unreachable!("assign handled above"),
}; };
let reg = self.build.binary( let reg = self.build.binary(
@ -329,12 +402,16 @@ fn lower_body<'cx>(
ret_ty: Ty<'cx>, ret_ty: Ty<'cx>,
) -> Result<Func<'cx>, Error> { ) -> Result<Func<'cx>, Error> {
let mut cx = FnLoweringCtxt { let mut cx = FnLoweringCtxt {
scopes: vec![FxHashMap::default()], scopes: vec![],
build: FuncBuilder::new(name, def_span, ret_ty, lcx), build: FuncBuilder::new(name, def_span, ret_ty, lcx),
lcx, lcx,
}; };
cx.lower_body(body)?; cx.lower_block(body)?;
if let Branch::Goto(BbIdx(u32::MAX)) = cx.build.cur_bb_mut().term {
cx.build.cur_bb_mut().term = Branch::Ret(Operand::Const(ConstValue::Void));
}
Ok(cx.build.finish()) Ok(cx.build.finish())
} }

View file

@ -23,7 +23,7 @@ impl<'a, 'cx> FuncBuilder<'a, 'cx> {
regs: Vec::new(), regs: Vec::new(),
bbs: vec![BasicBlock { bbs: vec![BasicBlock {
statements: Vec::new(), statements: Vec::new(),
term: Branch::Goto(BbIdx(0)), term: Branch::dummy(),
}], }],
name, name,
def_span, def_span,
@ -134,14 +134,12 @@ impl<'a, 'cx> FuncBuilder<'a, 'cx> {
pub fn new_block(&mut self) -> BbIdx { pub fn new_block(&mut self) -> BbIdx {
self.ir.bbs.push(BasicBlock { self.ir.bbs.push(BasicBlock {
statements: vec![], statements: vec![],
term: Branch::Goto(BbIdx(0)), term: Branch::dummy(),
}); });
BbIdx::from_usize(self.ir.bbs.len() - 1) BbIdx::from_usize(self.ir.bbs.len() - 1)
} }
pub fn finish(mut self) -> Func<'cx> { pub fn finish(self) -> Func<'cx> {
self.cur_bb_mut().term = Branch::Ret(Operand::Const(ConstValue::Void));
println!("{}", ir::func_to_string(&self.ir)); println!("{}", ir::func_to_string(&self.ir));
self.ir self.ir

View file

@ -484,6 +484,16 @@ where
return self.if_statement(); return self.if_statement();
} }
if let Some((_, span)) = eat!(self, Tok::Kw(Kw::Return)) {
if let Some((_, semi_span)) = eat!(self, Tok::Punct(P::Semicolon)) {
return Ok((Stmt::Return(None), span.extend(semi_span)));
} else {
let expr = self.expr()?;
let semi_span = expect!(self, Tok::Punct(P::Semicolon));
return Ok((Stmt::Return(Some(expr)), span.extend(semi_span)));
}
}
// it must be an expression stmt // it must be an expression stmt
let (expr, span) = self.expr()?; let (expr, span) = self.expr()?;
expect!(self, Tok::Punct(P::Semicolon)); expect!(self, Tok::Punct(P::Semicolon));

View file

@ -0,0 +1,36 @@
---
source: parser/src/parser/tests.rs
expression: "(parsed_pretty, pretty_printed_source)"
---
(
Ok([
(
FunctionDef(FunctionDef {
decl: Normal(NormalDecl {
decl_spec: DeclSpec {
ty: Integer(IntTy { sign: Signed, kind: Int }),
attrs: "(empty)",
},
init_declarators: [
(
InitDecl {
declarator: Declarator {
decl: WithParams {
ident: (main, 5..9),
params: [],
},
pointer: false,
},
init: None,
},
5..9,
),
],
}),
body: [(Return(None), 18..25)],
}),
1..27,
),
]),
"int main() {\n return\n}\n",
)

View file

@ -0,0 +1,36 @@
---
source: parser/src/parser/tests.rs
expression: "(parsed_pretty, pretty_printed_source)"
---
(
Ok([
(
FunctionDef(FunctionDef {
decl: Normal(NormalDecl {
decl_spec: DeclSpec {
ty: Integer(IntTy { sign: Signed, kind: Int }),
attrs: "(empty)",
},
init_declarators: [
(
InitDecl {
declarator: Declarator {
decl: WithParams {
ident: (main, 5..9),
params: [],
},
pointer: false,
},
init: None,
},
5..9,
),
],
}),
body: [(Return(Some((Atom(Int(0)), 25..26))), 18..27)],
}),
1..29,
),
]),
"int main() {\n return 0\n}\n",
)

View file

@ -202,3 +202,25 @@ int main() {
"# "#
); );
} }
#[test]
fn return_empty() {
parse_test!(
r#"
int main() {
return;
}
"#
);
}
#[test]
fn return_expr() {
parse_test!(
r#"
int main() {
return 0;
}
"#
);
}

View file

@ -298,7 +298,7 @@ impl CLexExt for u8 {
} }
fn is_c_identifier_digit(&self) -> bool { fn is_c_identifier_digit(&self) -> bool {
matches!(self, b'0'..=b'9') self.is_ascii_digit()
} }
fn is_c_whitespace(&self) -> bool { fn is_c_whitespace(&self) -> bool {

View file

@ -17,5 +17,11 @@ fn main() {
let arena = bumpalo::Bump::new(); let arena = bumpalo::Bump::new();
let _ = analysis::lower_translation_unit(&arena, &ast); let ir = analysis::lower_translation_unit(&arena, &ast);
match ir {
Ok(_) => {}
Err(err) => {
dbg!(err);
}
}
} }