From 2c838ab845bc54c5ef6cb0561332c84f34249368 Mon Sep 17 00:00:00 2001 From: Griffin Smith Date: Fri, 19 Mar 2021 20:46:19 -0400 Subject: feat(gs/achilles): Implement extern decls, for glibc functions Implement extern decls, which codegen to LLVM as forward-declared functions, and use these as a hook into calling glibc functions. We can print to the terminal now! The integration tests can test this now. Change-Id: I70af4546b417b888ad9fbb18798db240f77f4e71 Reviewed-on: https://cl.tvl.fyi/c/depot/+/2614 Tested-by: BuildkiteCI Reviewed-by: glittershark --- users/glittershark/achilles/ach/.gitignore | 1 + users/glittershark/achilles/ach/externs.ach | 5 +++ users/glittershark/achilles/src/ast/hir.rs | 20 +++++++++- users/glittershark/achilles/src/ast/mod.rs | 14 ++++++- users/glittershark/achilles/src/codegen/llvm.rs | 40 ++++++++++++++++++- users/glittershark/achilles/src/parser/mod.rs | 21 +++++++++- users/glittershark/achilles/src/parser/type_.rs | 8 ++-- users/glittershark/achilles/src/tc/mod.rs | 13 ++++++ users/glittershark/achilles/tests/compile.rs | 53 +++++++++++++++++-------- 9 files changed, 147 insertions(+), 28 deletions(-) create mode 100644 users/glittershark/achilles/ach/externs.ach (limited to 'users/glittershark') diff --git a/users/glittershark/achilles/ach/.gitignore b/users/glittershark/achilles/ach/.gitignore index 683a53a01f..07b37e1689 100644 --- a/users/glittershark/achilles/ach/.gitignore +++ b/users/glittershark/achilles/ach/.gitignore @@ -3,3 +3,4 @@ functions simple +externs diff --git a/users/glittershark/achilles/ach/externs.ach b/users/glittershark/achilles/ach/externs.ach new file mode 100644 index 0000000000..faf8ce90e3 --- /dev/null +++ b/users/glittershark/achilles/ach/externs.ach @@ -0,0 +1,5 @@ +extern puts : fn cstring -> int + +fn main = + let _ = puts "foobar" + in 0 diff --git a/users/glittershark/achilles/src/ast/hir.rs b/users/glittershark/achilles/src/ast/hir.rs index 6859174a2d..0212b3dbcd 100644 --- a/users/glittershark/achilles/src/ast/hir.rs +++ b/users/glittershark/achilles/src/ast/hir.rs @@ -219,12 +219,19 @@ pub enum Decl<'a, T> { body: Box>, type_: T, }, + + Extern { + name: Ident<'a>, + arg_types: Vec, + ret_type: T, + }, } impl<'a, T> Decl<'a, T> { - pub fn type_(&self) -> &T { + pub fn type_(&self) -> Option<&T> { match self { - Decl::Fun { type_, .. } => type_, + Decl::Fun { type_, .. } => Some(type_), + Decl::Extern { .. } => None, } } @@ -247,6 +254,15 @@ impl<'a, T> Decl<'a, T> { body: Box::new(body.traverse_type(f.clone())?), type_: f(type_)?, }), + Decl::Extern { + name, + arg_types, + ret_type, + } => Ok(Decl::Extern { + name, + arg_types: arg_types.into_iter().map(f.clone()).try_collect()?, + ret_type: f(ret_type)?, + }), } } } diff --git a/users/glittershark/achilles/src/ast/mod.rs b/users/glittershark/achilles/src/ast/mod.rs index 3a2261aeda..22d16c9364 100644 --- a/users/glittershark/achilles/src/ast/mod.rs +++ b/users/glittershark/achilles/src/ast/mod.rs @@ -265,8 +265,18 @@ impl<'a> Fun<'a> { #[derive(Debug, PartialEq, Eq, Clone)] pub enum Decl<'a> { - Fun { name: Ident<'a>, body: Fun<'a> }, - Ascription { name: Ident<'a>, type_: Type<'a> }, + Fun { + name: Ident<'a>, + body: Fun<'a>, + }, + Ascription { + name: Ident<'a>, + type_: Type<'a>, + }, + Extern { + name: Ident<'a>, + type_: FunctionType<'a>, + }, } //// diff --git a/users/glittershark/achilles/src/codegen/llvm.rs b/users/glittershark/achilles/src/codegen/llvm.rs index 9b580d3c45..f49e084a81 100644 --- a/users/glittershark/achilles/src/codegen/llvm.rs +++ b/users/glittershark/achilles/src/codegen/llvm.rs @@ -9,7 +9,7 @@ use inkwell::module::Module; use inkwell::support::LLVMString; use inkwell::types::{BasicType, BasicTypeEnum, FunctionType, IntType}; use inkwell::values::{AnyValueEnum, BasicValueEnum, FunctionValue}; -use inkwell::IntPredicate; +use inkwell::{AddressSpace, IntPredicate}; use thiserror::Error; use crate::ast::hir::{Binding, Decl, Expr}; @@ -249,6 +249,26 @@ impl<'ctx, 'ast> Codegen<'ctx, 'ast> { Ok(self.finish_function(&res)) } + pub fn codegen_extern( + &mut self, + name: &str, + args: &'ast [Type], + ret: &'ast Type, + ) -> Result<()> { + self.module.add_function( + name, + self.codegen_type(ret).fn_type( + &args + .iter() + .map(|t| self.codegen_type(t)) + .collect::>(), + false, + ), + None, + ); + Ok(()) + } + pub fn codegen_decl(&mut self, decl: &'ast Decl<'ast, Type>) -> Result<()> { match decl { Decl::Fun { @@ -257,6 +277,11 @@ impl<'ctx, 'ast> Codegen<'ctx, 'ast> { self.codegen_function(name.into(), args, body)?; Ok(()) } + Decl::Extern { + name, + arg_types, + ret_type, + } => self.codegen_extern(name.into(), arg_types, ret_type), } } @@ -274,7 +299,18 @@ impl<'ctx, 'ast> Codegen<'ctx, 'ast> { fn codegen_type(&self, type_: &'ast Type) -> BasicTypeEnum<'ctx> { // TODO - self.context.i64_type().into() + match type_ { + Type::Int => self.context.i64_type().into(), + Type::Float => self.context.f64_type().into(), + Type::Bool => self.context.bool_type().into(), + Type::CString => self + .context + .i8_type() + .ptr_type(AddressSpace::Generic) + .into(), + Type::Function(_) => todo!(), + Type::Var(_) => unreachable!(), + } } fn codegen_int_type(&self, type_: &'ast Type) -> IntType<'ctx> { diff --git a/users/glittershark/achilles/src/parser/mod.rs b/users/glittershark/achilles/src/parser/mod.rs index dd7874aff8..c7e541ce48 100644 --- a/users/glittershark/achilles/src/parser/mod.rs +++ b/users/glittershark/achilles/src/parser/mod.rs @@ -9,6 +9,7 @@ mod type_; use crate::ast::{Arg, Decl, Fun, Ident}; pub use expr::expr; +use type_::function_type; pub use type_::type_; pub type Error = nom::Err>; @@ -79,9 +80,24 @@ named!(arg(&str) -> Arg, alt!( ascripted_arg )); +named!(extern_decl(&str) -> Decl, do_parse!( + complete!(tag!("extern")) + >> multispace1 + >> name: ident + >> multispace0 + >> char!(':') + >> multispace0 + >> type_: function_type + >> multispace0 + >> (Decl::Extern { + name, + type_ + }) +)); + named!(fun_decl(&str) -> Decl, do_parse!( complete!(tag!("fn")) - >> multispace0 + >> multispace1 >> name: ident >> multispace1 >> args: separated_list0!(multispace1, arg) @@ -112,7 +128,8 @@ named!(ascription_decl(&str) -> Decl, do_parse!( named!(pub decl(&str) -> Decl, alt!( ascription_decl | - fun_decl + fun_decl | + extern_decl )); named!(pub toplevel(&str) -> Vec, terminated!(many0!(decl), multispace0)); diff --git a/users/glittershark/achilles/src/parser/type_.rs b/users/glittershark/achilles/src/parser/type_.rs index c90ceda4d7..1e6e380bb8 100644 --- a/users/glittershark/achilles/src/parser/type_.rs +++ b/users/glittershark/achilles/src/parser/type_.rs @@ -4,7 +4,7 @@ use nom::{alt, delimited, do_parse, map, named, opt, separated_list0, tag, termi use super::ident; use crate::ast::{FunctionType, Type}; -named!(function_type(&str) -> Type, do_parse!( +named!(pub function_type(&str) -> FunctionType, do_parse!( tag!("fn") >> multispace1 >> args: map!(opt!(terminated!(separated_list0!( @@ -18,10 +18,10 @@ named!(function_type(&str) -> Type, do_parse!( >> tag!("->") >> multispace1 >> ret: type_ - >> (Type::Function(FunctionType { + >> (FunctionType { args, ret: Box::new(ret) - })) + }) )); named!(pub type_(&str) -> Type, alt!( @@ -29,7 +29,7 @@ named!(pub type_(&str) -> Type, alt!( tag!("float") => { |_| Type::Float } | tag!("bool") => { |_| Type::Bool } | tag!("cstring") => { |_| Type::CString } | - function_type | + function_type => { |ft| Type::Function(ft) }| ident => { |id| Type::Var(id) } | delimited!( tuple!(tag!("("), multispace0), diff --git a/users/glittershark/achilles/src/tc/mod.rs b/users/glittershark/achilles/src/tc/mod.rs index 52c18e6d53..4bca52733b 100644 --- a/users/glittershark/achilles/src/tc/mod.rs +++ b/users/glittershark/achilles/src/tc/mod.rs @@ -337,6 +337,19 @@ impl<'ast> Typechecker<'ast> { self.env.set(name.clone(), type_); Ok(None) } + ast::Decl::Extern { name, type_ } => { + let type_ = self.type_from_ast_type(ast::Type::Function(type_)); + self.env.set(name.clone(), type_.clone()); + let (arg_types, ret_type) = match type_ { + Type::Fun { args, ret } => (args, *ret), + _ => unreachable!(), + }; + Ok(Some(hir::Decl::Extern { + name, + arg_types, + ret_type, + })) + } } } diff --git a/users/glittershark/achilles/tests/compile.rs b/users/glittershark/achilles/tests/compile.rs index 7fa15ad965..1b4da463a9 100644 --- a/users/glittershark/achilles/tests/compile.rs +++ b/users/glittershark/achilles/tests/compile.rs @@ -2,7 +2,30 @@ use std::process::Command; use crate_root::root; -const FIXTURES: &[(&str, i32)] = &[("simple", 5), ("functions", 9)]; +struct Fixture { + name: &'static str, + exit_code: i32, + expected_output: &'static str, +} + +const FIXTURES: &[Fixture] = &[ + Fixture { + name: "simple", + exit_code: 5, + expected_output: "", + }, + // TODO(grfn): needs monomorphization + // Fixture { + // name: "functions", + // exit_code: 9, + // expected_output: "", + // }, + Fixture { + name: "externs", + exit_code: 0, + expected_output: "foobar\n", + }, +]; #[test] fn compile_and_run_files() { @@ -21,13 +44,18 @@ fn compile_and_run_files() { "make clean failed" ); - for (fixture, exit_code) in FIXTURES { - println!(">>> Testing: {}", fixture); + for Fixture { + name, + exit_code, + expected_output, + } in FIXTURES + { + println!(">>> Testing: {}", name); - println!(" Running: `make {}`", fixture); + println!(" Running: `make {}`", name); assert!( Command::new("make") - .arg(fixture) + .arg(name) .current_dir(&ach) .spawn() .unwrap() @@ -37,18 +65,11 @@ fn compile_and_run_files() { "make failed" ); - let out_path = ach.join(fixture); + let out_path = ach.join(name); println!(" Running: `{}`", out_path.to_str().unwrap()); - assert_eq!( - Command::new(out_path) - .spawn() - .unwrap() - .wait() - .unwrap() - .code() - .unwrap(), - *exit_code, - ); + let output = Command::new(out_path).output().unwrap(); + assert_eq!(output.status.code().unwrap(), *exit_code,); + assert_eq!(output.stdout, expected_output.as_bytes()); println!(" OK"); } } -- cgit 1.4.1