use std::convert::{TryFrom, TryInto}; use std::path::Path; use std::result; use inkwell::basic_block::BasicBlock; use inkwell::builder::Builder; pub use inkwell::context::Context; use inkwell::module::Module; use inkwell::support::LLVMString; use inkwell::types::{BasicType, BasicTypeEnum, FunctionType, IntType, StructType}; use inkwell::values::{AnyValueEnum, BasicValueEnum, FunctionValue, StructValue}; use inkwell::{AddressSpace, IntPredicate}; use itertools::Itertools; use thiserror::Error; use crate::ast::hir::{Binding, Decl, Expr, Pattern}; use crate::ast::{BinaryOperator, Ident, Literal, Type, UnaryOperator}; use crate::common::env::Env; #[derive(Debug, PartialEq, Eq, Error)] pub enum Error { #[error("Undefined variable {0}")] UndefinedVariable(Ident<'static>), #[error("LLVM Error: {0}")] LLVMError(String), } impl From<LLVMString> for Error { fn from(s: LLVMString) -> Self { Self::LLVMError(s.to_string()) } } pub type Result<T> = result::Result<T, Error>; pub struct Codegen<'ctx, 'ast> { context: &'ctx Context, pub module: Module<'ctx>, builder: Builder<'ctx>, env: Env<&'ast Ident<'ast>, AnyValueEnum<'ctx>>, function_stack: Vec<FunctionValue<'ctx>>, identifier_counter: u32, } impl<'ctx, 'ast> Codegen<'ctx, 'ast> { pub fn new(context: &'ctx Context, module_name: &str) -> Self { let module = context.create_module(module_name); let builder = context.create_builder(); Self { context, module, builder, env: Default::default(), function_stack: Default::default(), identifier_counter: 0, } } pub fn new_function<'a>( &'a mut self, name: &str, ty: FunctionType<'ctx>, ) -> &'a FunctionValue<'ctx> { self.function_stack .push(self.module.add_function(name, ty, None)); let basic_block = self.append_basic_block("entry"); self.builder.position_at_end(basic_block); self.function_stack.last().unwrap() } pub fn finish_function(&mut self, res: Option<&BasicValueEnum<'ctx>>) -> FunctionValue<'ctx> { self.builder.build_return(match res { // lol Some(val) => Some(val), None => None, }); self.function_stack.pop().unwrap() } pub fn append_basic_block(&self, name: &str) -> BasicBlock<'ctx> { self.context .append_basic_block(*self.function_stack.last().unwrap(), name) } fn bind_pattern(&mut self, pat: &'ast Pattern<'ast, Type>, val: AnyValueEnum<'ctx>) { match pat { Pattern::Id(id, _) => self.env.set(id, val), Pattern::Tuple(pats) => { for (i, pat) in pats.iter().enumerate() { let member = self .builder .build_extract_value( StructValue::try_from(val).unwrap(), i as _, "pat_bind", ) .unwrap(); self.bind_pattern(pat, member.into()); } } } } pub fn codegen_expr( &mut self, expr: &'ast Expr<'ast, Type>, ) -> Result<Option<AnyValueEnum<'ctx>>> { match expr { Expr::Ident(id, _) => self .env .resolve(id) .cloned() .ok_or_else(|| Error::UndefinedVariable(id.to_owned())) .map(Some), Expr::Literal(lit, ty) => { let ty = self.codegen_int_type(ty); match lit { Literal::Int(i) => Ok(Some(AnyValueEnum::IntValue(ty.const_int(*i, false)))), Literal::Bool(b) => Ok(Some(AnyValueEnum::IntValue( ty.const_int(if *b { 1 } else { 0 }, false), ))), Literal::String(s) => Ok(Some( self.builder .build_global_string_ptr(s, "s") .as_pointer_value() .into(), )), Literal::Unit => Ok(None), } } Expr::UnaryOp { op, rhs, .. } => { let rhs = self.codegen_expr(rhs)?.unwrap(); match op { UnaryOperator::Not => unimplemented!(), UnaryOperator::Neg => Ok(Some(AnyValueEnum::IntValue( self.builder.build_int_neg(rhs.into_int_value(), "neg"), ))), } } Expr::BinaryOp { lhs, op, rhs, .. } => { let lhs = self.codegen_expr(lhs)?.unwrap(); let rhs = self.codegen_expr(rhs)?.unwrap(); match op { BinaryOperator::Add => { Ok(Some(AnyValueEnum::IntValue(self.builder.build_int_add( lhs.into_int_value(), rhs.into_int_value(), "add", )))) } BinaryOperator::Sub => { Ok(Some(AnyValueEnum::IntValue(self.builder.build_int_sub( lhs.into_int_value(), rhs.into_int_value(), "add", )))) } BinaryOperator::Mul => { Ok(Some(AnyValueEnum::IntValue(self.builder.build_int_sub( lhs.into_int_value(), rhs.into_int_value(), "add", )))) } BinaryOperator::Div => Ok(Some(AnyValueEnum::IntValue( self.builder.build_int_signed_div( lhs.into_int_value(), rhs.into_int_value(), "add", ), ))), BinaryOperator::Pow => unimplemented!(), BinaryOperator::Equ => Ok(Some(AnyValueEnum::IntValue( self.builder.build_int_compare( IntPredicate::EQ, lhs.into_int_value(), rhs.into_int_value(), "eq", ), ))), BinaryOperator::Neq => todo!(), } } Expr::Let { bindings, body, .. } => { self.env.push(); for Binding { pat, body, .. } in bindings { if let Some(val) = self.codegen_expr(body)? { self.bind_pattern(pat, val); } } let res = self.codegen_expr(body); self.env.pop(); res } Expr::If { condition, then, else_, type_, } => { let then_block = self.append_basic_block("then"); let else_block = self.append_basic_block("else"); let join_block = self.append_basic_block("join"); let condition = self.codegen_expr(condition)?.unwrap(); self.builder.build_conditional_branch( condition.into_int_value(), then_block, else_block, ); self.builder.position_at_end(then_block); let then_res = self.codegen_expr(then)?; self.builder.build_unconditional_branch(join_block); self.builder.position_at_end(else_block); let else_res = self.codegen_expr(else_)?; self.builder.build_unconditional_branch(join_block); self.builder.position_at_end(join_block); if let Some(phi_type) = self.codegen_type(type_) { let phi = self.builder.build_phi(phi_type, "join"); phi.add_incoming(&[ ( &BasicValueEnum::try_from(then_res.unwrap()).unwrap(), then_block, ), ( &BasicValueEnum::try_from(else_res.unwrap()).unwrap(), else_block, ), ]); Ok(Some(phi.as_basic_value().into())) } else { Ok(None) } } Expr::Call { fun, args, .. } => { if let Expr::Ident(id, _) = &**fun { let function = self .module .get_function(id.into()) .or_else(|| self.env.resolve(id)?.clone().try_into().ok()) .ok_or_else(|| Error::UndefinedVariable(id.to_owned()))?; let args = args .iter() .map(|arg| Ok(self.codegen_expr(arg)?.unwrap().try_into().unwrap())) .collect::<Result<Vec<_>>>()?; Ok(self .builder .build_call(function, &args, "call") .try_as_basic_value() .left() .map(|val| val.into())) } else { todo!() } } Expr::Fun { args, body, .. } => { let fname = self.fresh_ident("f"); let cur_block = self.builder.get_insert_block().unwrap(); let env = self.env.save(); // TODO: closures let function = self.codegen_function(&fname, args, body)?; self.builder.position_at_end(cur_block); self.env.restore(env); Ok(Some(function.into())) } Expr::Tuple(members, ty) => { let values = members .into_iter() .map(|expr| self.codegen_expr(expr)) .collect::<Result<Vec<_>>>()? .into_iter() .filter_map(|x| x) .map(|x| x.try_into().unwrap()) .collect_vec(); let field_types = ty.as_tuple().unwrap(); let tuple_type = self.codegen_tuple_type(field_types); Ok(Some(tuple_type.const_named_struct(&values).into())) } } } pub fn codegen_function( &mut self, name: &str, args: &'ast [(Ident<'ast>, Type)], body: &'ast Expr<'ast, Type>, ) -> Result<FunctionValue<'ctx>> { let arg_types = args .iter() .filter_map(|(_, at)| self.codegen_type(at)) .collect::<Vec<_>>(); self.new_function( name, match self.codegen_type(body.type_()) { Some(ret_ty) => ret_ty.fn_type(&arg_types, false), None => self.context.void_type().fn_type(&arg_types, false), }, ); self.env.push(); for (i, (arg, _)) in args.iter().enumerate() { self.env.set( arg, self.cur_function().get_nth_param(i as u32).unwrap().into(), ); } let res = self.codegen_expr(body)?; self.env.pop(); Ok(self.finish_function(res.map(|av| av.try_into().unwrap()).as_ref())) } pub fn codegen_extern( &mut self, name: &str, args: &'ast [Type], ret: &'ast Type, ) -> Result<()> { let arg_types = args .iter() .map(|t| self.codegen_type(t).unwrap()) .collect::<Vec<_>>(); self.module.add_function( name, match self.codegen_type(ret) { Some(ret_ty) => ret_ty.fn_type(&arg_types, false), None => self.context.void_type().fn_type(&arg_types, false), }, None, ); Ok(()) } pub fn codegen_decl(&mut self, decl: &'ast Decl<'ast, Type>) -> Result<()> { match decl { Decl::Fun { name, args, body, .. } => { self.codegen_function(name.into(), args, body)?; Ok(()) } Decl::Extern { name, arg_types, ret_type, } => self.codegen_extern(name.into(), arg_types, ret_type), } } pub fn codegen_main(&mut self, expr: &'ast Expr<'ast, Type>) -> Result<()> { self.new_function("main", self.context.i64_type().fn_type(&[], false)); let res = self.codegen_expr(expr)?; if *expr.type_() != Type::Int { self.builder .build_return(Some(&self.context.i64_type().const_int(0, false))); } else { self.finish_function(res.map(|r| r.try_into().unwrap()).as_ref()); } Ok(()) } fn codegen_type(&self, type_: &'ast Type) -> Option<BasicTypeEnum<'ctx>> { // TODO match type_ { Type::Int => Some(self.context.i64_type().into()), Type::Float => Some(self.context.f64_type().into()), Type::Bool => Some(self.context.bool_type().into()), Type::CString => Some( self.context .i8_type() .ptr_type(AddressSpace::Generic) .into(), ), Type::Function(_) => todo!(), Type::Var(_) => unreachable!(), Type::Unit => None, Type::Tuple(ts) => Some(self.codegen_tuple_type(ts).into()), } } fn codegen_tuple_type(&self, ts: &'ast [Type]) -> StructType<'ctx> { self.context.struct_type( ts.iter() .filter_map(|t| self.codegen_type(t)) .collect_vec() .as_slice(), false, ) } fn codegen_int_type(&self, type_: &'ast Type) -> IntType<'ctx> { // TODO self.context.i64_type() } pub fn print_to_file<P>(&self, path: P) -> Result<()> where P: AsRef<Path>, { Ok(self.module.print_to_file(path)?) } pub fn binary_to_file<P>(&self, path: P) -> Result<()> where P: AsRef<Path>, { if self.module.write_bitcode_to_path(path.as_ref()) { Ok(()) } else { Err(Error::LLVMError( "Error writing bitcode to output path".to_owned(), )) } } fn fresh_ident(&mut self, prefix: &str) -> String { self.identifier_counter += 1; format!("{}{}", prefix, self.identifier_counter) } fn cur_function(&self) -> &FunctionValue<'ctx> { self.function_stack.last().unwrap() } } #[cfg(test)] mod tests { use inkwell::execution_engine::JitFunction; use inkwell::OptimizationLevel; use super::*; fn jit_eval<T>(expr: &str) -> anyhow::Result<T> { let expr = crate::parser::expr(expr).unwrap().1; let expr = crate::tc::typecheck_expr(expr).unwrap(); let context = Context::create(); let mut codegen = Codegen::new(&context, "test"); let execution_engine = codegen .module .create_jit_execution_engine(OptimizationLevel::None) .unwrap(); codegen.codegen_function("test", &[], &expr)?; unsafe { let fun: JitFunction<unsafe extern "C" fn() -> T> = execution_engine.get_function("test")?; Ok(fun.call()) } } #[test] fn add_literals() { assert_eq!(jit_eval::<i64>("1 + 2").unwrap(), 3); } #[test] fn variable_shadowing() { assert_eq!( jit_eval::<i64>("let x = 1 in (let x = 2 in x) + x").unwrap(), 3 ); } #[test] fn eq() { assert_eq!( jit_eval::<i64>("let x = 1 in if x == 1 then 2 else 4").unwrap(), 2 ); } #[test] fn function_call() { let res = jit_eval::<i64>("let id = fn x = x in id 1").unwrap(); assert_eq!(res, 1); } #[test] fn bind_tuple_pattern() { let res = jit_eval::<i64>("let (x, y) = (1, 2) in x + y").unwrap(); assert_eq!(res, 3); } }