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 {
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
// 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 crate::ast::{Expr, ExprKind, Stmt, Type};
use crate::ast::{Expr, ExprKind, Op, Type};
use super::{type_to_llvm, LlvmIrCodegen};
pub struct ExprLlvm<'a>(String, &'a Expr<Type>);
impl<'a> ExprLlvm<'a> {
pub fn name(&self) -> &str {
pub fn as_str(&self) -> &str {
&self.0
}
}
@ -20,27 +20,80 @@ impl<W: Write> LlvmIrCodegen<W> {
expr: &'a Expr<Type>,
) -> Result<ExprLlvm<'a>> {
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 {
ExprKind::Int(n) => {
writeln!(self.writer, "%{} = alloca {}", expr_id, ty_str)?;
writeln!(
self.writer,
"store {} {}, {}* %{}",
ty_str, n, ty_str, expr_id
)?;
expr_ref = format!("{}", n);
// writeln!(self.writer, "%{} = alloca {}", expr_id, ty_str)?;
// writeln!(
// self.writer,
// "store {} {}, {}* %{}",
// ty_str, n, ty_str, expr_id
// )?;
}
ExprKind::Var(name) => match self.var_env.lookup(name) {
Some((_, name)) => {
expr_id = name.clone();
expr_ref = name.clone();
}
None => bail!("Unbound name {name:?}"),
},
ExprKind::BinOp(_, _, _) => todo!(),
ExprKind::Call(_, _) => todo!(),
ExprKind::BinOp(left, op, right) => {
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;
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> {
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
for func in program.iter().filter_map(|decl| match decl {
Decl::Func(v) => Some(v),
_ => None,
}) {
// Write the function header
println!("Func type: {:?}", func.ty);
let (func_args_ty, ret_ty) = match &func.ty {
let (_, ret_ty) = match &func.ty {
Type::Func(args, ret) => (args, ret),
_ => 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!(
self.writer,
"define {} @{} () {{",
"define {} {} ({}) {{",
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)?;
self.var_env.push();
self.convert_stmts(&func.stmts)?;
self.var_env.pop();
writeln!(self.writer, "}}")?;
self.var_env.pop();
}
Ok(())

View file

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

View file

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