functions

This commit is contained in:
nora 2023-05-23 22:06:18 +02:00
parent dba217c18e
commit f321d0e9e1
7 changed files with 248 additions and 158 deletions

116
analysis/src/ctxt.rs Normal file
View file

@ -0,0 +1,116 @@
use std::cell::{Cell, RefCell};
use parser::{
ast::{self, IntTyKind},
Symbol,
};
use rustc_hash::{FxHashMap, FxHashSet};
use crate::{
ir::{DefId, Layout, TyLayout, VariableInfo},
ty::{Ty, TyKind},
};
#[derive(Debug)]
pub(crate) struct LoweringCx<'cx> {
tys: RefCell<FxHashSet<&'cx TyKind<'cx>>>,
layouts: RefCell<FxHashSet<&'cx Layout>>,
string_literals: RefCell<FxHashMap<&'cx [u8], DefId>>,
pub(crate) arena: &'cx bumpalo::Bump,
next_def_id: Cell<DefId>,
/**/
pub(crate) global_decls: FxHashMap<Symbol, VariableInfo<'cx>>,
}
impl<'cx> LoweringCx<'cx> {
pub(crate) fn new(arena: &'cx bumpalo::Bump) -> Self {
LoweringCx {
tys: RefCell::default(),
layouts: RefCell::default(),
string_literals: RefCell::default(),
arena,
next_def_id: Cell::new(DefId(0)),
global_decls: FxHashMap::default(),
}
}
pub(crate) 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
}
pub(crate) fn lower_ty(&self, ty: &ast::TypeSpecifier) -> Ty<'cx> {
let kind = match ty {
ast::TypeSpecifier::Void => TyKind::Void,
ast::TypeSpecifier::Char => TyKind::Char,
ast::TypeSpecifier::Integer(int) => TyKind::Int(*int),
ast::TypeSpecifier::Float => TyKind::Float,
ast::TypeSpecifier::Double => TyKind::Double,
ast::TypeSpecifier::LongDouble => TyKind::LongDouble,
};
self.intern_ty(kind)
}
pub(crate) fn intern_ty(&self, kind: TyKind<'cx>) -> Ty<'cx> {
let opt_kind = self.tys.borrow().get(&kind).copied();
match opt_kind {
Some(ty) => Ty::new_unchecked(ty),
None => {
let kind = self.arena.alloc(kind);
self.tys.borrow_mut().insert(kind);
Ty::new_unchecked(kind)
}
}
}
fn intern_layout(&self, layout: Layout) -> &'cx Layout {
let opt_layout = self.layouts.borrow().get(&layout).copied();
match opt_layout {
Some(layout) => layout,
None => {
let layout = self.arena.alloc(layout);
self.layouts.borrow_mut().insert(layout);
layout
}
}
}
pub(crate) fn intern_str_lit(&self, str: &[u8]) -> DefId {
let opt_str = self.string_literals.borrow().get(str).copied();
match opt_str {
Some(lit_def_id) => lit_def_id,
None => {
let str = self.arena.alloc_slice_copy(str);
let lit_def_id = self.next_def_id();
self.string_literals.borrow_mut().insert(str, lit_def_id);
lit_def_id
}
}
}
pub(crate) fn layout_of(&self, ty: Ty<'cx>) -> TyLayout<'cx> {
let layout = match *ty {
TyKind::Void => Layout::size_align(0, 1),
TyKind::Char => Layout::size_align(1, 1),
TyKind::Int(int) => match int.1 {
IntTyKind::Bool => Layout::size_align(1, 1),
IntTyKind::Char => Layout::size_align(1, 1),
IntTyKind::Short => Layout::size_align(2, 2),
IntTyKind::Int => Layout::size_align(4, 4),
IntTyKind::Long => Layout::size_align(8, 8),
IntTyKind::LongLong => Layout::size_align(8, 8),
},
TyKind::Float => Layout::size_align(4, 4),
TyKind::Double => Layout::size_align(8, 8),
TyKind::LongDouble => Layout::size_align(8, 8),
TyKind::Func(_, _) => Layout::size_align(8, 8),
TyKind::Struct(_) => todo!("layout_of struct"),
TyKind::Union(_) => todo!("layout_of union"),
TyKind::Enum(_) => todo!("layout_of enum"),
TyKind::Ptr(_) => Layout::size_align(8, 8),
};
let layout = self.intern_layout(layout);
TyLayout { ty, layout }
}
}

View file

@ -36,13 +36,29 @@ mod validate;
use std::fmt::{Debug, Display};
use parser::{Span, Symbol};
use parser::{ast, Span, Symbol};
pub use pretty::{func_to_string, ir_to_string};
use rustc_hash::FxHashMap;
pub use validate::validate;
use crate::ty::Ty;
#[derive(Debug)]
pub struct VariableInfo<'cx> {
pub def_span: Span,
// TODO: a static is definitely not a "local" in any way!! deal with that stuff!!!
pub decl_attr: ast::DeclAttr,
pub tyl: TyLayout<'cx>,
pub kind: VariableInfoKind,
}
#[derive(Debug)]
pub enum VariableInfoKind {
Local { ptr_to: Register },
FnDef { def_id: DefId },
Static { def_id: DefId },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DefId(pub u32);
@ -70,7 +86,7 @@ pub struct Func<'cx> {
pub def_span: Span,
pub ret_ty: Ty<'cx>,
/// The amount of function parameters. regs[..arity] are the parameters.
pub arity: u32,
pub arity: usize,
}
#[derive(Clone, Copy)]
@ -105,14 +121,14 @@ pub enum StatementKind {
align: Operand,
},
Store {
ptr_reg: Register,
ptr: Operand,
value: Operand,
size: Operand,
align: Operand,
},
Load {
result: Register,
ptr_reg: Register,
ptr: Operand,
size: Operand,
align: Operand,
},

View file

@ -37,8 +37,13 @@ impl<W: Write> PrettyPrinter<W> {
write!(self.out, "def {}(", func.name)?;
for param in 0..func.arity {
let reg = &func.regs[param as usize];
write!(self.out, "{} {}", reg.tyl.ty, print_reg(Register(param)))?;
let reg = &func.regs[param];
write!(
self.out,
"{} {}",
reg.tyl.ty,
print_reg(Register(param as _))
)?;
if (param + 1) != func.arity {
write!(self.out, ", ")?;
}
@ -70,28 +75,28 @@ impl<W: Write> PrettyPrinter<W> {
)
}
StatementKind::Store {
ptr_reg,
ptr: ptr_reg,
value,
size,
align,
} => writeln!(
self.out,
" store {}, {}, size={}, align={}",
print_reg(ptr_reg),
print_op(ptr_reg),
print_op(value),
print_op(size),
print_op(align)
),
StatementKind::Load {
result,
ptr_reg,
ptr: ptr_reg,
size,
align,
} => writeln!(
self.out,
" {} = load {}, size={}, align={}",
print_reg(result),
print_reg(ptr_reg),
print_op(ptr_reg),
print_op(size),
print_op(align)
),

View file

@ -1,6 +1,7 @@
#![allow(dead_code)] // TODO: no
#![warn(rust_2018_idioms)]
mod ctxt;
mod ir;
mod lower;
mod ty;

View file

@ -1,19 +1,18 @@
mod builder;
mod typeck;
use std::cell::{Cell, RefCell};
use parser::{
ast::{self, ExprBinary, IntSign, IntTy, IntTyKind},
Span, Symbol,
};
use rustc_hash::{FxHashMap, FxHashSet};
use rustc_hash::FxHashMap;
use self::{builder::FuncBuilder, typeck::Coercion};
use crate::{
ctxt::LoweringCx,
ir::{
self, BbIdx, BinKind, Branch, ConstValue, DefId, Func, Ir, Layout, Operand, Register,
TyLayout, UnaryKind,
self, BbIdx, BinKind, Branch, ConstValue, Func, Ir, Operand, Register, TyLayout, UnaryKind,
VariableInfo, VariableInfoKind,
},
ty::{Ty, TyKind},
Error,
@ -21,106 +20,11 @@ use crate::{
type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Debug)]
struct LoweringCx<'cx> {
tys: RefCell<FxHashSet<&'cx TyKind<'cx>>>,
layouts: RefCell<FxHashSet<&'cx Layout>>,
string_literals: RefCell<FxHashMap<&'cx [u8], DefId>>,
arena: &'cx bumpalo::Bump,
next_def_id: Cell<DefId>,
}
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: &ast::TypeSpecifier) -> Ty<'cx> {
let kind = match ty {
ast::TypeSpecifier::Void => TyKind::Void,
ast::TypeSpecifier::Char => TyKind::Char,
ast::TypeSpecifier::Integer(int) => TyKind::Int(*int),
ast::TypeSpecifier::Float => TyKind::Float,
ast::TypeSpecifier::Double => TyKind::Double,
ast::TypeSpecifier::LongDouble => TyKind::LongDouble,
};
self.intern_ty(kind)
}
fn intern_ty(&self, kind: TyKind<'cx>) -> Ty<'cx> {
let opt_kind = self.tys.borrow().get(&kind).copied();
match opt_kind {
Some(ty) => Ty::new_unchecked(ty),
None => {
let kind = self.arena.alloc(kind);
self.tys.borrow_mut().insert(kind);
Ty::new_unchecked(kind)
}
}
}
fn intern_layout(&self, layout: Layout) -> &'cx Layout {
let opt_layout = self.layouts.borrow().get(&layout).copied();
match opt_layout {
Some(layout) => layout,
None => {
let layout = self.arena.alloc(layout);
self.layouts.borrow_mut().insert(layout);
layout
}
}
}
fn intern_str_lit(&self, str: &[u8]) -> DefId {
let opt_str = self.string_literals.borrow().get(str).copied();
match opt_str {
Some(lit_def_id) => lit_def_id,
None => {
let str = self.arena.alloc_slice_copy(str);
let lit_def_id = self.next_def_id();
self.string_literals.borrow_mut().insert(str, lit_def_id);
lit_def_id
}
}
}
fn layout_of(&self, ty: Ty<'cx>) -> TyLayout<'cx> {
let layout = match *ty {
TyKind::Void => Layout::size_align(0, 1),
TyKind::Char => Layout::size_align(1, 1),
TyKind::Int(int) => match int.1 {
IntTyKind::Bool => Layout::size_align(1, 1),
IntTyKind::Char => Layout::size_align(1, 1),
IntTyKind::Short => Layout::size_align(2, 2),
IntTyKind::Int => Layout::size_align(4, 4),
IntTyKind::Long => Layout::size_align(8, 8),
IntTyKind::LongLong => Layout::size_align(8, 8),
},
TyKind::Float => Layout::size_align(4, 4),
TyKind::Double => Layout::size_align(8, 8),
TyKind::LongDouble => Layout::size_align(8, 8),
TyKind::Struct(_) => todo!("layout_of struct"),
TyKind::Union(_) => todo!("layout_of union"),
TyKind::Enum(_) => todo!("layout_of enum"),
TyKind::Ptr(_) => Layout::size_align(8, 8),
};
let layout = self.intern_layout(layout);
TyLayout { ty, layout }
}
}
pub fn lower_translation_unit<'cx>(
arena: &'cx bumpalo::Bump,
ast: &ast::TranslationUnit,
) -> Result<Ir<'cx>, Error> {
let lcx = LoweringCx {
tys: RefCell::default(),
layouts: RefCell::default(),
string_literals: RefCell::default(),
arena,
next_def_id: Cell::new(DefId(0)),
};
let mut lcx = LoweringCx::new(arena);
let mut ir = Ir {
funcs: FxHashMap::default(),
@ -140,8 +44,28 @@ pub fn lower_translation_unit<'cx>(
unreachable!("function def needs withparams declarator");
};
let def_id = lcx.next_def_id();
let func = lower_func(&lcx, body, def_span, ident.0, ret_ty, params)?;
ir.funcs.insert(lcx.next_def_id(), func);
let args = &*lcx
.arena
.alloc_slice_fill_iter(func.regs[..func.arity].iter().map(|data| data.tyl.ty));
let ty = lcx.intern_ty(TyKind::Func(args, func.ret_ty));
let def_span = func.def_span;
ir.funcs.insert(def_id, func);
lcx.global_decls.insert(
ident.0,
VariableInfo {
def_span,
decl_attr: ast::DeclAttr::empty(),
tyl: lcx.layout_of(ty),
kind: VariableInfoKind::FnDef { def_id },
},
);
}
}
}
@ -182,7 +106,11 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
}
fn resolve_ident(&self, ident: Symbol) -> Option<&VariableInfo<'cx>> {
self.scopes.iter().rev().find_map(|s| s.get(&ident))
self.scopes
.iter()
.rev()
.find_map(|s| s.get(&ident))
.or_else(|| self.lcx.global_decls.get(&ident))
}
fn lower_block(&mut self, body: &[(ast::Stmt, Span)]) -> Result<()> {
@ -206,9 +134,9 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
let variable_info = VariableInfo {
def_span: *def_span,
ptr_to,
decl_attr,
tyl: tyl.clone(),
tyl,
kind: VariableInfoKind::Local { ptr_to },
};
let predeclared = self.scopes.last_mut().unwrap().insert(name, variable_info);
if let Some(predeclared) = predeclared {
@ -220,20 +148,29 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
}
if let Some((init, init_span)) = &var.init {
let init = self.lower_expr(init, *init_span)?;
self.build.store(ptr_to, init.0, tyl.layout, *init_span);
self.build
.store(Operand::Reg(ptr_to), init.0, tyl.layout, *init_span);
}
}
Ok(())
}
fn expr_as_lvalue(&mut self, expr: &ast::Expr) -> Result<Register> {
fn expr_as_lvalue(&mut self, expr: &ast::Expr) -> Result<(Operand, TyLayout<'cx>)> {
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)
Ok(match var.kind {
VariableInfoKind::Local { ptr_to } => (Operand::Reg(ptr_to), var.tyl),
VariableInfoKind::FnDef { def_id } => {
(Operand::Const(ConstValue::StaticPtr(def_id)), var.tyl)
}
VariableInfoKind::Static { def_id } => {
(Operand::Const(ConstValue::StaticPtr(def_id)), var.tyl)
}
})
}
fn lower_stmt(&mut self, stmt: &ast::Stmt, stmt_span: Span) -> Result<()> {
@ -300,14 +237,8 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
todo!("assign operation");
}
let rhs = self.lower_expr(&rhs.0, rhs.1)?;
let (ast::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.0, var.tyl.layout, stmt_span);
let (ptr_to, tyl) = self.expr_as_lvalue(&lhs.0)?;
self.build.store(ptr_to, rhs.0, tyl.layout, stmt_span);
}
ast::Stmt::Expr(expr) => {
self.lower_expr(expr, stmt_span)?;
@ -333,9 +264,24 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
return Err(Error::new(format!("cannot find variable {ident}"), *ident_span));
};
let tyl = var.tyl;
let op = self.build.load(var.tyl, var.ptr_to, span);
match var.kind {
VariableInfoKind::Local { ptr_to } => {
let op = self.build.load(var.tyl, Operand::Reg(ptr_to), span);
(Operand::Reg(op), tyl)
}
VariableInfoKind::FnDef { def_id } => {
(Operand::Const(ConstValue::StaticPtr(def_id)), tyl)
}
VariableInfoKind::Static { def_id } => {
let op = self.build.load(
var.tyl,
Operand::Const(ConstValue::StaticPtr(def_id)),
span,
);
(Operand::Reg(op), tyl)
}
}
}
ast::Expr::Atom(ast::Atom::String(string)) => {
let lit_def_id = self.lcx.intern_str_lit(string);
(
@ -362,15 +308,15 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
));
}
let lvalue = self.expr_as_lvalue(&rhs_expr.0)?;
let (lvalue, tyl) = self.expr_as_lvalue(&rhs_expr.0)?;
let bin_kind = if is_incr { BinKind::Add } else { BinKind::Sub };
let lhs = self.build.load(self.dummy_tyl(), lvalue, span);
let result =
self.build
.binary(bin_kind, Operand::Reg(lhs), rhs, span, self.dummy_tyl());
self.build.store(lhs, rhs, self.dummy_tyl().layout, span);
let lhs = self.build.load(tyl, lvalue, span);
let result = self
.build
.binary(bin_kind, Operand::Reg(lhs), rhs, span, tyl);
self.build.store(Operand::Reg(lhs), rhs, tyl.layout, span);
(Operand::Reg(result), self.dummy_tyl())
(Operand::Reg(result), tyl)
}
ast::Expr::Unary(unary) => {
let rhs = self.lower_expr(&unary.rhs.0, unary.rhs.1)?;
@ -398,9 +344,8 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
}
let rhs = self.lower_expr(&rhs.0, rhs.1)?;
let ptr_to = self.expr_as_lvalue(&lhs.0)?;
self.build
.store(ptr_to, rhs.0, self.dummy_tyl().layout, span);
let (ptr_to, tyl) = self.expr_as_lvalue(&lhs.0)?;
self.build.store(ptr_to, rhs.0, tyl.layout, span);
rhs
}
ast::Expr::Binary(ExprBinary {
@ -443,11 +388,10 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
ast::ArithOpKind::BitOr => BinKind::BitOr,
};
let reg = self
.build
.binary(kind, lhs, rhs, span, self.lcx.layout_of(result));
let result = self.lcx.layout_of(result);
let reg = self.build.binary(kind, lhs, rhs, span, result);
(Operand::Reg(reg), self.dummy_tyl())
(Operand::Reg(reg), result)
}
ast::Expr::Binary(ExprBinary {
op: ast::BinaryOp::Comparison(comp),
@ -506,14 +450,6 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
}
}
#[derive(Debug)]
struct VariableInfo<'cx> {
def_span: Span,
tyl: TyLayout<'cx>,
ptr_to: Register,
decl_attr: ast::DeclAttr,
}
fn lower_func<'cx>(
// may be used later
lcx: &LoweringCx<'cx>,
@ -561,9 +497,9 @@ fn lower_func<'cx>(
let variable_info = VariableInfo {
def_span: span,
ptr_to,
decl_attr,
tyl,
kind: VariableInfoKind::Local { ptr_to },
};
let predeclared = cx.scopes.last_mut().unwrap().insert(name, variable_info);
if let Some(predeclared) = predeclared {
@ -573,8 +509,12 @@ fn lower_func<'cx>(
);
}
cx.build
.store(ptr_to, Operand::Reg(Register(i as _)), tyl.layout, span);
cx.build.store(
Operand::Reg(ptr_to),
Operand::Reg(Register(i as _)),
tyl.layout,
span,
);
}
cx.lower_block(body)?;

View file

@ -22,7 +22,7 @@ impl<'a, 'cx> FuncBuilder<'a, 'cx> {
def_span: Span,
ret_ty: Ty<'cx>,
lcx: &'a LoweringCx<'cx>,
arity: u32,
arity: usize,
) -> Self {
Self {
ir: Func {
@ -104,11 +104,11 @@ impl<'a, 'cx> FuncBuilder<'a, 'cx> {
reg
}
pub fn load(&mut self, tyl: TyLayout<'cx>, ptr_reg: Register, span: Span) -> Register {
pub fn load(&mut self, tyl: TyLayout<'cx>, ptr: Operand, span: Span) -> Register {
let reg = self.new_reg(None, tyl.clone());
let stmt = StatementKind::Load {
result: reg,
ptr_reg,
ptr,
size: Operand::const_u64(tyl.layout.size),
align: Operand::const_u64(tyl.layout.align),
};
@ -118,9 +118,9 @@ impl<'a, 'cx> FuncBuilder<'a, 'cx> {
reg
}
pub fn store(&mut self, ptr_reg: Register, rhs: Operand, layout: &Layout, span: Span) {
pub fn store(&mut self, ptr: Operand, rhs: Operand, layout: &Layout, span: Span) {
let stmt = StatementKind::Store {
ptr_reg,
ptr,
value: rhs,
size: Operand::const_u64(layout.size),
align: Operand::const_u64(layout.align),

View file

@ -24,6 +24,7 @@ pub enum TyKind<'cx> {
Double,
LongDouble,
Ptr(Ty<'cx>),
Func(&'cx [Ty<'cx>], Ty<'cx>),
Union(UnionTy<'cx>),
Struct(StructTy<'cx>),
Enum(EnumTy),
@ -96,6 +97,17 @@ impl Display for Ty<'_> {
TyKind::Ptr(ty) => {
write!(f, "{ty}*")
}
TyKind::Func(args, ret) => {
write!(f, "{ret}(")?;
for (i, arg) in args.iter().enumerate() {
write!(f, "{arg}")?;
if (i + 1) != args.len() {
write!(f, ", ")?;
}
}
write!(f, ")")?;
Ok(())
}
TyKind::Union(_) => todo!(),
TyKind::Struct(_) => todo!(),
TyKind::Enum(_) => todo!(),