Functions

This commit is contained in:
Michael Zhang 2022-07-17 22:06:37 -05:00
parent 221beea478
commit 090589fc3e
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
6 changed files with 173 additions and 56 deletions

2
.editorconfig Normal file
View file

@ -0,0 +1,2 @@
[*.rs]
max_line_length = 80

View file

@ -64,10 +64,13 @@ pub enum Op {
} }
impl Op { impl Op {
pub fn check_types(&self, left_ty: &Type, right_ty: &Type) -> bool { pub fn check_types(&self, left_ty: &Type, right_ty: &Type) -> Option<Type> {
// TODO: since only binops work on integers right now, just check that the two sides are both // TODO: since only binops work on integers right now, just check that the two sides are both
// integers. this will have to change once && gets added. // integers. this will have to change once && gets added.
matches!(left_ty, Type::Int) && matches!(right_ty, Type::Int) match (left_ty, right_ty) {
(Type::Int, Type::Int) => Some(Type::Int),
_ => None,
}
} }
} }

View file

@ -2,14 +2,14 @@ use std::io::Write;
use anyhow::Result; use anyhow::Result;
use crate::ast::{Expr, ExprKind, Stmt, Type}; use crate::ast::{Expr, ExprKind, Op, Type};
use super::{type_to_llvm, LlvmIrCodegen}; use super::{type_to_llvm, LlvmIrCodegen};
pub struct ExprLlvm<'a>(String, &'a Expr<Type>); pub struct ExprLlvm<'a>(String, &'a Expr<Type>);
impl<'a> ExprLlvm<'a> { impl<'a> ExprLlvm<'a> {
pub fn name(&self) -> &str { pub fn as_str(&self) -> &str {
&self.0 &self.0
} }
} }
@ -20,27 +20,80 @@ impl<W: Write> LlvmIrCodegen<W> {
expr: &'a Expr<Type>, expr: &'a Expr<Type>,
) -> Result<ExprLlvm<'a>> { ) -> Result<ExprLlvm<'a>> {
let ty_str = type_to_llvm(&expr.ty); let ty_str = type_to_llvm(&expr.ty);
let mut expr_id = self.gensym("expr"); let mut expr_ref = self.gensym(Some("%"), "expr");
match &expr.kind { match &expr.kind {
ExprKind::Int(n) => { ExprKind::Int(n) => {
writeln!(self.writer, "%{} = alloca {}", expr_id, ty_str)?; expr_ref = format!("{}", n);
writeln!( // writeln!(self.writer, "%{} = alloca {}", expr_id, ty_str)?;
self.writer, // writeln!(
"store {} {}, {}* %{}", // self.writer,
ty_str, n, ty_str, expr_id // "store {} {}, {}* %{}",
)?; // ty_str, n, ty_str, expr_id
// )?;
} }
ExprKind::Var(name) => match self.var_env.lookup(name) { ExprKind::Var(name) => match self.var_env.lookup(name) {
Some((_, name)) => { Some((_, name)) => {
expr_id = name.clone(); expr_ref = name.clone();
} }
None => bail!("Unbound name {name:?}"), None => bail!("Unbound name {name:?}"),
}, },
ExprKind::BinOp(_, _, _) => todo!(), ExprKind::BinOp(left, op, right) => {
ExprKind::Call(_, _) => todo!(), let result_ty = match op.check_types(&left.ty, &right.ty) {
Some(v) => type_to_llvm(&v),
None => bail!("Invalid types on operation."),
};
let left = self.convert_expr(left)?;
let right = self.convert_expr(right)?;
match op {
Op::LessThan => todo!(),
Op::GreaterThan => todo!(),
Op::Plus => writeln!(
self.writer,
"{} = add {} {}, {}",
expr_ref,
result_ty,
left.as_str(),
right.as_str(),
)?,
}
}
ExprKind::Call(func, args) => match self.var_env.lookup(func) {
Some((Type::Func(func_args_ty, func_ret_ty), func_name)) => {
// Clone these so we aren't depending on a reference
let func_args_ty = func_args_ty.clone();
let func_ret_ty = func_ret_ty.clone();
let func_name = func_name.to_owned();
let mut args_str = Vec::new();
for (call_arg, expected_ty) in args.iter().zip(func_args_ty.iter()) {
let call_ty = &call_arg.ty;
if call_ty != expected_ty {
bail!("Type error: expected {expected_ty:?}, got {call_ty:?}");
}
let expr = self.convert_expr(&call_arg)?;
let call_ty_str = type_to_llvm(&call_ty);
args_str.push(format!("{} {}", call_ty_str, expr.as_str()));
}
let ret_ty_str = type_to_llvm(&func_ret_ty);
writeln!(
self.writer,
"{} = call {} {} ({})",
expr_ref,
ret_ty_str,
func_name,
args_str.join(", ")
)?;
}
_ => bail!("No function with name {func:?}"),
},
} }
Ok(ExprLlvm(expr_id, expr)) Ok(ExprLlvm(expr_ref, expr))
} }
} }

View file

@ -27,10 +27,19 @@ impl<W> LlvmIrCodegen<W> {
} }
} }
pub fn gensym(&mut self, name: impl AsRef<str>) -> String { pub fn gensym(
&mut self,
prefix: Option<&str>,
name: impl AsRef<str>,
) -> String {
let ctr = self.ctr; let ctr = self.ctr;
self.ctr += 1; self.ctr += 1;
format!("{}.{}", name.as_ref(), ctr) format!(
"{}{}.{}",
prefix.map(|s| s.as_ref()).unwrap_or(""),
name.as_ref(),
ctr
)
} }
} }
@ -45,30 +54,67 @@ fn type_to_llvm(ty: &Type) -> String {
impl<W: Write> CodegenBackend for LlvmIrCodegen<W> { impl<W: Write> CodegenBackend for LlvmIrCodegen<W> {
fn convert(&mut self, program: Vec<Decl<Type>>) -> Result<()> { fn convert(&mut self, program: Vec<Decl<Type>>) -> Result<()> {
// First, create a global environment and add all functions, so they can be
// called within other functions
//
// This is in a separate loop so there's no dependency on function order
self.var_env = LayeredEnv::new();
for func in program.iter().filter_map(|decl| match decl {
Decl::Func(v) => Some(v),
_ => None,
}) {
let func_name = match func.name.as_str() {
"main" => String::from("@main"),
_ => self.gensym(Some("@"), format!("func.{}", func.name)),
};
let value = (func.ty.clone(), func_name);
self.var_env.insert(func.name.clone(), value);
}
// Convert all functions // Convert all functions
for func in program.iter().filter_map(|decl| match decl { for func in program.iter().filter_map(|decl| match decl {
Decl::Func(v) => Some(v), Decl::Func(v) => Some(v),
_ => None, _ => None,
}) { }) {
// Write the function header let (_, ret_ty) = match &func.ty {
println!("Func type: {:?}", func.ty);
let (func_args_ty, ret_ty) = match &func.ty {
Type::Func(args, ret) => (args, ret), Type::Func(args, ret) => (args, ret),
_ => unreachable!(""), _ => unreachable!(""),
}; };
let (_, func_name) =
self.var_env.lookup(&func.name).expect("Just inserted.");
let func_name = func_name.to_owned();
// Set the global environment by adding the arguments
let mut args_str = Vec::new();
self.var_env.push();
for arg in func.args.iter() {
let arg_name = self.gensym(Some("%"), &arg.name);
let arg_ty_str = type_to_llvm(&arg.ty);
args_str.push(format!("{} {}", arg_ty_str, arg_name));
self
.var_env
.insert(arg.name.clone(), (arg.ty.clone(), arg_name));
}
// Write the function header
writeln!( writeln!(
self.writer, self.writer,
"define {} @{} () {{", "define {} {} ({}) {{",
type_to_llvm(ret_ty), type_to_llvm(ret_ty),
func.name func_name,
args_str.join(", "),
)?; )?;
let entry_block_name = self.gensym("entry");
// Write the first block
let entry_block_name = self.gensym(None, "entry");
writeln!(self.writer, "{}:", entry_block_name)?; writeln!(self.writer, "{}:", entry_block_name)?;
self.var_env.push();
self.convert_stmts(&func.stmts)?; self.convert_stmts(&func.stmts)?;
self.var_env.pop();
writeln!(self.writer, "}}")?; writeln!(self.writer, "}}")?;
self.var_env.pop();
} }
Ok(()) Ok(())

View file

@ -18,25 +18,14 @@ impl<W: Write> LlvmIrCodegen<W> {
Stmt::Let(name, _, expr) => { Stmt::Let(name, _, expr) => {
let ty = expr.ty.clone(); let ty = expr.ty.clone();
let expr = self.convert_expr(&expr)?; let expr = self.convert_expr(&expr)?;
let new_name = expr.name().to_owned(); let new_name = expr.as_str().to_owned();
// TODO:
self.var_env.insert(name.clone(), (ty, new_name)); self.var_env.insert(name.clone(), (ty, new_name));
} }
Stmt::Return(expr) => match expr { Stmt::Return(expr) => match expr {
Some(ret_val) => { Some(ret_val) => {
let ret_ty = type_to_llvm(&ret_val.ty); let ret_ty = type_to_llvm(&ret_val.ty);
let ret_val = self.convert_expr(ret_val)?; let ret_val = self.convert_expr(ret_val)?;
let ret_name = self.gensym("ret"); writeln!(self.writer, "ret {} {}", ret_ty, ret_val.as_str())?;
writeln!(
self.writer,
"%{} = load {}, {}* %{}",
ret_name,
ret_ty,
ret_ty,
ret_val.name()
)?;
writeln!(self.writer, "ret {} %{}", ret_ty, ret_name)?;
} }
None => writeln!(self.writer, "ret void")?, None => writeln!(self.writer, "ret void")?,
}, },

View file

@ -1,36 +1,34 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::hash::Hash; use std::hash::Hash;
#[derive(Debug)]
pub struct LayeredEnv<K, V> { pub struct LayeredEnv<K, V> {
parent: Option<Box<LayeredEnv<K, V>>>, inner: Option<InnerEnv<K, V>>,
inner_map: HashMap<K, V>,
} }
impl<K: Hash + Eq, V> LayeredEnv<K, V> { #[derive(Debug)]
pub fn new() -> Self { struct InnerEnv<K, V> {
LayeredEnv { parent: Option<Box<InnerEnv<K, V>>>,
map: HashMap<K, V>,
}
impl<K: Hash + Eq, V> InnerEnv<K, V> {
fn new() -> Self {
InnerEnv {
parent: None, parent: None,
inner_map: HashMap::new(), map: HashMap::new(),
} }
} }
pub fn insert(&mut self, key: K, value: V) { fn with_parent(parent: InnerEnv<K, V>) -> Self {
self.inner_map.insert(key, value); InnerEnv {
} parent: Some(Box::new(parent)),
map: HashMap::new(),
pub fn push(self) -> LayeredEnv<K, V> {
LayeredEnv {
parent: Some(Box::new(self)),
inner_map: HashMap::new(),
} }
} }
pub fn pop(self) -> LayeredEnv<K, V> { fn lookup(&self, key: &K) -> Option<&V> {
*self.parent.unwrap() match self.map.get(key) {
}
pub fn lookup(&self, key: &K) -> Option<&V> {
match self.inner_map.get(key) {
Some(v) => Some(v), Some(v) => Some(v),
None => match self.parent.as_ref() { None => match self.parent.as_ref() {
Some(p) => p.lookup(key), Some(p) => p.lookup(key),
@ -39,3 +37,29 @@ impl<K: Hash + Eq, V> LayeredEnv<K, V> {
} }
} }
} }
impl<K: Hash + Eq, V> LayeredEnv<K, V> {
pub fn new() -> Self {
LayeredEnv {
inner: Some(InnerEnv::new()),
}
}
pub fn insert(&mut self, key: K, value: V) {
self.inner.as_mut().unwrap().map.insert(key, value);
}
pub fn push(&mut self) {
let inner = self.inner.take().unwrap();
self.inner = Some(InnerEnv::with_parent(inner));
}
pub fn pop(&mut self) {
let inner = self.inner.take().unwrap();
self.inner = Some(*inner.parent.unwrap());
}
pub fn lookup(&self, key: &K) -> Option<&V> {
self.inner.as_ref().unwrap().lookup(key)
}
}