diff --git a/.gitignore b/.gitignore index 51d5d10..f856ac9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ result* *.bc *.ll +*.ast a.out .direnv + diff --git a/Cargo.lock b/Cargo.lock index 4c2327c..fc2b31a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,12 +64,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "cc" -version = "1.0.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" - [[package]] name = "cfg-if" version = "1.0.0" @@ -154,9 +148,9 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", - "inkwell", "lalrpop", "lalrpop-util", + "tempfile", ] [[package]] @@ -174,6 +168,15 @@ dependencies = [ "log", ] +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + [[package]] name = "fixedbitset" version = "0.4.2" @@ -223,26 +226,12 @@ dependencies = [ ] [[package]] -name = "inkwell" -version = "0.1.0" -source = "git+https://github.com/TheDan64/inkwell?branch=master#25b9fc5870370211504e874e7c81dc53573bca79" +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "either", - "inkwell_internals", - "libc", - "llvm-sys", - "once_cell", - "parking_lot", -] - -[[package]] -name = "inkwell_internals" -version = "0.5.0" -source = "git+https://github.com/TheDan64/inkwell?branch=master#25b9fc5870370211504e874e7c81dc53573bca79" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "cfg-if", ] [[package]] @@ -286,31 +275,12 @@ dependencies = [ "regex", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" -[[package]] -name = "llvm-sys" -version = "120.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b716322964966a62377cf86e64f00ca7043505fdf27bd2ec7d41ae6682d1e7" -dependencies = [ - "cc", - "lazy_static", - "libc", - "regex", - "semver", -] - [[package]] name = "lock_api" version = "0.4.7" @@ -377,15 +347,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "pest" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" -dependencies = [ - "ucd-trie", -] - [[package]] name = "petgraph" version = "0.6.2" @@ -496,6 +457,15 @@ version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + [[package]] name = "rustversion" version = "1.0.8" @@ -508,24 +478,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - [[package]] name = "siphasher" version = "0.3.10" @@ -568,6 +520,20 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + [[package]] name = "term" version = "0.7.0" @@ -623,12 +589,6 @@ dependencies = [ "crunchy", ] -[[package]] -name = "ucd-trie" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89570599c4fe5585de2b388aab47e99f7fa4e9238a1399f707a02e356058141c" - [[package]] name = "unicode-ident" version = "1.0.2" diff --git a/Cargo.toml b/Cargo.toml index a5fc013..ab0afa3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,11 +7,7 @@ edition = "2021" anyhow = "1.0.56" clap = { version = "3.1.8", features = ["derive"] } lalrpop-util = { version = "0.19.7", features = ["lexer"] } - -[dependencies.inkwell] -git = "https://github.com/TheDan64/inkwell" -branch = "master" -features = ["llvm12-0"] +tempfile = "3.3.0" [build-dependencies] lalrpop = "0.19.7" diff --git a/default.nix b/default.nix index 7c95555..ccecb20 100644 --- a/default.nix +++ b/default.nix @@ -1,6 +1,4 @@ -{ fenix, makeRustPlatform, lib, nix-gitignore, llvmPkgs, libffi }: - -with llvmPkgs; +{ fenix, makeRustPlatform, lib, nix-gitignore, libffi, libgcc }: let rustPlatform = makeRustPlatform { inherit (fenix.minimal) cargo rustc; }; @@ -8,15 +6,8 @@ in rustPlatform.buildRustPackage { name = "e0"; src = nix-gitignore.gitignoreSource [ ./.gitignore ] ./.; - nativeBuildInputs = [ llvm.dev ]; - buildInputs = [ libffi llvm ]; + nativeBuildInputs = [ ]; + buildInputs = [ libffi ]; - LLVM_SYS_120_PREFIX = "${llvm.dev}"; - - cargoLock = { - lockFile = ./Cargo.lock; - outputHashes = { - "inkwell-0.1.0" = "sha256-+ih3SO0n6YmZ/mcf+rLDwPAy/1MEZ/A+tI4pM1pUhvU="; - }; - }; + cargoLock = { lockFile = ./Cargo.lock; }; } diff --git a/examples/conditions.e0 b/examples/conditions.e0 index 1e0d918..08cf616 100644 --- a/examples/conditions.e0 +++ b/examples/conditions.e0 @@ -1,11 +1,17 @@ fn main() -> int { - let x = 5; + let x = 25; - if x < 3 { - return 3; - } else if x > 10 { + if x < 10 { return 10; + } else if x > 50 { + return 50; } else { - return x; + if x < 20 { + return 20; + } else if x > 40 { + return 40; + } else { + return x; + } } } diff --git a/flake.nix b/flake.nix index 9540222..81e7862 100644 --- a/flake.nix +++ b/flake.nix @@ -12,16 +12,17 @@ inherit system; overlays = [ fenix.overlay ]; }; - # https://github.com/NixOS/nixpkgs/issues/148117 - llvmPkgs = pkgs.pkgsStatic.llvmPackages_12; - myPkgs = rec { e0 = pkgs.callPackage ./. { inherit llvmPkgs; }; }; + myPkgs = rec { e0 = pkgs.callPackage ./. { }; }; in rec { devShell = pkgs.mkShell { - packages = with pkgs; [ llvmPkgs.clangUseLLVM cargo-watch ]; + packages = with pkgs; + with pkgs.llvmPackages_12; [ + cargo-watch + clangUseLLVM + ]; inputsFrom = with myPkgs; [ e0 ]; CARGO_UNSTABLE_SPARSE_REGISTRY = "true"; - LLVM_SYS_120_PREFIX = "${llvmPkgs.llvm.dev}"; }; packages = utils.lib.flattenTree myPkgs; diff --git a/src/ast/cranelift.rs b/src/ast/cranelift.rs new file mode 100644 index 0000000..e65aa29 --- /dev/null +++ b/src/ast/cranelift.rs @@ -0,0 +1,12 @@ +use anyhow::Result; +use cranelift_codegen::Context as ClifContext; + +use super::{Decl, Type}; + +pub fn convert( + file_name: String, + context: &Context, + program: Vec>, +) -> Result { + todo!() +} diff --git a/src/ast/llvm.rs b/src/ast/llvm.rs index 8db9f10..0b106d7 100644 --- a/src/ast/llvm.rs +++ b/src/ast/llvm.rs @@ -6,10 +6,7 @@ use inkwell::{ context::Context, module::Module, types::{BasicMetadataTypeEnum, BasicTypeEnum, FunctionType}, - values::{ - BasicMetadataValueEnum, BasicValue, BasicValueEnum, FunctionValue, - IntValue, PointerValue, - }, + values::{BasicValue, BasicValueEnum, FunctionValue, IntValue}, IntPredicate, }; diff --git a/src/ast/mod.rs b/src/ast/mod.rs index bede50b..9605293 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1,4 +1,5 @@ -pub mod llvm; +// pub mod llvm; +// pub mod cranelift; pub mod typed; #[derive(Debug)] diff --git a/src/codegen/llvm_ir/expr.rs b/src/codegen/llvm_ir/expr.rs new file mode 100644 index 0000000..da1de0e --- /dev/null +++ b/src/codegen/llvm_ir/expr.rs @@ -0,0 +1,41 @@ +use std::io::Write; + +use anyhow::Result; + +use crate::ast::{Expr, ExprKind, Stmt, Type}; + +use super::{type_to_llvm, LlvmIrCodegen}; + +pub struct ExprLlvm<'a>(String, &'a Expr); + +impl<'a> ExprLlvm<'a> { + pub fn name(&self) -> &str { + &self.0 + } +} + +impl LlvmIrCodegen { + pub(super) fn convert_expr<'a>( + &mut self, + expr: &'a Expr, + ) -> Result> { + let expr_id = self.gensym("expr"); + + match &expr.kind { + ExprKind::Int(n) => { + let ty_str = type_to_llvm(&expr.ty); + writeln!(self.writer, "%{} = alloca {}", expr_id, ty_str)?; + writeln!( + self.writer, + "store {} {}, {}* %{}", + ty_str, n, ty_str, expr_id + )?; + } + ExprKind::Var(_) => todo!(), + ExprKind::BinOp(_, _, _) => todo!(), + ExprKind::Call(_, _) => todo!(), + } + + Ok(ExprLlvm(expr_id, expr)) + } +} diff --git a/src/codegen/llvm_ir/if_else.rs b/src/codegen/llvm_ir/if_else.rs new file mode 100644 index 0000000..701e433 --- /dev/null +++ b/src/codegen/llvm_ir/if_else.rs @@ -0,0 +1,19 @@ +use std::io::Write; + +use anyhow::Result; + +use crate::ast::{IfElse, Stmt, Type}; + +use super::LlvmIrCodegen; + +impl LlvmIrCodegen { + pub(super) fn convert_if_else( + &mut self, + if_else: &IfElse, + ) -> Result<()> { + // Build condition + let cond = self.convert_expr(&if_else.cond); + + todo!() + } +} diff --git a/src/codegen/llvm_ir/mod.rs b/src/codegen/llvm_ir/mod.rs new file mode 100644 index 0000000..6bab4a5 --- /dev/null +++ b/src/codegen/llvm_ir/mod.rs @@ -0,0 +1,76 @@ +mod expr; +mod if_else; +mod stmts; + +use std::io::Write; + +use anyhow::Result; + +use crate::ast::{Decl, Type}; +use crate::utils::LayeredEnv; + +use super::CodegenBackend; + +pub struct LlvmIrCodegen { + ctr: usize, + env: LayeredEnv, + writer: W, +} + +impl LlvmIrCodegen { + pub fn new(writer: W) -> Self { + let env = LayeredEnv::default(); + LlvmIrCodegen { + ctr: 0, + env, + writer, + } + } + + pub fn gensym(&mut self, name: impl AsRef) -> String { + let ctr = self.ctr; + self.ctr += 1; + format!("{}.{}", name.as_ref(), ctr) + } +} + +fn type_to_llvm(ty: &Type) -> String { + match ty { + Type::Int => String::from("i32"), + Type::Bool => String::from("i1"), + Type::StructInst(_) => todo!(), + Type::Func(_, _) => todo!(), + } +} + +impl CodegenBackend for LlvmIrCodegen { + fn convert(&mut self, program: Vec>) -> Result<()> { + // 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 { + Type::Func(args, ret) => (args, ret), + _ => unreachable!(""), + }; + + writeln!( + self.writer, + "define {} @{} () {{", + type_to_llvm(ret_ty), + func.name + )?; + let entry_block_name = self.gensym("entry"); + writeln!(self.writer, "{}:", entry_block_name)?; + + self.convert_stmts(&func.stmts)?; + + writeln!(self.writer, "}}")?; + } + + Ok(()) + } +} diff --git a/src/codegen/llvm_ir/stmts.rs b/src/codegen/llvm_ir/stmts.rs new file mode 100644 index 0000000..3bc1f99 --- /dev/null +++ b/src/codegen/llvm_ir/stmts.rs @@ -0,0 +1,42 @@ +use std::io::Write; + +use anyhow::Result; + +use crate::ast::{Stmt, Type}; + +use super::{type_to_llvm, LlvmIrCodegen}; + +impl LlvmIrCodegen { + pub(super) fn convert_stmts( + &mut self, + stmts: impl AsRef<[Stmt]>, + ) -> Result<()> { + let stmts = stmts.as_ref(); + + for stmt in stmts.iter() { + match stmt { + Stmt::Let(_, _, _) => todo!(), + 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)?; + } + None => writeln!(self.writer, "ret void")?, + }, + Stmt::IfElse(if_else) => self.convert_if_else(if_else)?, + } + } + + Ok(()) + } +} diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs new file mode 100644 index 0000000..09ac3bf --- /dev/null +++ b/src/codegen/mod.rs @@ -0,0 +1,9 @@ +pub mod llvm_ir; + +use anyhow::Result; + +use crate::ast::{Decl, Type}; + +pub trait CodegenBackend { + fn convert(&mut self, program: Vec>) -> Result<()>; +} diff --git a/src/main.rs b/src/main.rs index 0d68e27..6002daf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,15 +6,17 @@ extern crate anyhow; lalrpop_mod!(parser); mod ast; +pub mod codegen; mod utils; use std::fs::{self, File}; +use std::io::Write; use std::path::PathBuf; use anyhow::Result; use clap::Parser; -use inkwell::context::Context; +use crate::codegen::{llvm_ir::LlvmIrCodegen, CodegenBackend}; use crate::parser::ProgramParser; #[derive(Debug, Parser)] @@ -40,15 +42,27 @@ fn main() -> Result<()> { let typed_ast = ast::typed::convert(ast)?; - let context = Context::create(); - let module = - ast::llvm::convert(opts.path.display().to_string(), &context, typed_ast)?; + if let Some(path) = opts.emit_ast { + let mut file = File::create(&path)?; + file.write(format!("{:#?}", typed_ast).as_bytes())?; + } { let file = File::create(&opts.out_path)?; - module.write_bitcode_to_file(&file, true, true); - println!("Emitted."); + let mut codegen = LlvmIrCodegen::new(file); + codegen.convert(typed_ast)?; } + // let ctx = ast::cranelift::convert( + // opts.path.display().to_string(), + // typed_ast, + // )?; + + // { + // let file = File::create(&opts.out_path)?; + // ctx.compile_and_emit(); + // println!("Emitted."); + // } + Ok(()) } diff --git a/src/utils.rs b/src/utils.rs index e5e26ed..6e9baa2 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,36 +1,35 @@ -// TODO: put layered environment here? +use std::collections::HashMap; +use std::hash::Hash; -use inkwell::{ - types::{BasicMetadataTypeEnum, BasicTypeEnum}, - values::{BasicMetadataValueEnum, BasicValueEnum}, -}; - -pub fn convert_type_to_metadata_type( - a: BasicTypeEnum, -) -> BasicMetadataTypeEnum { - use BasicMetadataTypeEnum as B; - use BasicTypeEnum as A; - match a { - A::IntType(a) => B::IntType(a), - A::ArrayType(a) => B::ArrayType(a), - A::FloatType(a) => B::FloatType(a), - A::StructType(a) => B::StructType(a), - A::VectorType(a) => B::VectorType(a), - A::PointerType(a) => B::PointerType(a), - } +#[derive(Default)] +pub struct LayeredEnv { + parent: Option>>, + inner_map: HashMap, } -pub fn convert_value_to_metadata_value( - a: BasicValueEnum, -) -> BasicMetadataValueEnum { - use BasicMetadataValueEnum as B; - use BasicValueEnum as A; - match a { - A::ArrayValue(a) => B::ArrayValue(a), - A::IntValue(a) => B::IntValue(a), - A::FloatValue(a) => B::FloatValue(a), - A::PointerValue(a) => B::PointerValue(a), - A::StructValue(a) => B::StructValue(a), - A::VectorValue(a) => B::VectorValue(a), +impl LayeredEnv { + pub fn insert(&mut self, key: K, value: V) { + self.inner_map.insert(key, value); + } + + pub fn push(self) -> LayeredEnv { + LayeredEnv { + parent: Some(Box::new(self)), + inner_map: HashMap::new(), + } + } + + pub fn pop(self) -> LayeredEnv { + *self.parent.unwrap() + } + + pub fn lookup(&self, key: &K) -> Option<&V> { + match self.inner_map.get(key) { + Some(v) => Some(v), + None => match self.parent.as_ref() { + Some(p) => p.lookup(key), + None => None, + }, + } } }