Write to .ll files

This commit is contained in:
Michael Zhang 2022-07-17 21:00:19 -05:00
parent 4d6ad1bb2c
commit 16cde4377c
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
16 changed files with 314 additions and 148 deletions

2
.gitignore vendored
View file

@ -3,6 +3,8 @@ result*
*.bc
*.ll
*.ast
a.out
.direnv

116
Cargo.lock generated
View file

@ -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"

View file

@ -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"

View file

@ -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; };
}

View file

@ -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 {
if x < 20 {
return 20;
} else if x > 40 {
return 40;
} else {
return x;
}
}
}

View file

@ -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;

12
src/ast/cranelift.rs Normal file
View file

@ -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<Decl<Type>>,
) -> Result<ClifContext> {
todo!()
}

View file

@ -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,
};

View file

@ -1,4 +1,5 @@
pub mod llvm;
// pub mod llvm;
// pub mod cranelift;
pub mod typed;
#[derive(Debug)]

View file

@ -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<Type>);
impl<'a> ExprLlvm<'a> {
pub fn name(&self) -> &str {
&self.0
}
}
impl<W: Write> LlvmIrCodegen<W> {
pub(super) fn convert_expr<'a>(
&mut self,
expr: &'a Expr<Type>,
) -> Result<ExprLlvm<'a>> {
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))
}
}

View file

@ -0,0 +1,19 @@
use std::io::Write;
use anyhow::Result;
use crate::ast::{IfElse, Stmt, Type};
use super::LlvmIrCodegen;
impl<W: Write> LlvmIrCodegen<W> {
pub(super) fn convert_if_else(
&mut self,
if_else: &IfElse<Type>,
) -> Result<()> {
// Build condition
let cond = self.convert_expr(&if_else.cond);
todo!()
}
}

View file

@ -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<W> {
ctr: usize,
env: LayeredEnv<String, ()>,
writer: W,
}
impl<W> LlvmIrCodegen<W> {
pub fn new(writer: W) -> Self {
let env = LayeredEnv::default();
LlvmIrCodegen {
ctr: 0,
env,
writer,
}
}
pub fn gensym(&mut self, name: impl AsRef<str>) -> 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<W: Write> CodegenBackend for LlvmIrCodegen<W> {
fn convert(&mut self, program: Vec<Decl<Type>>) -> 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(())
}
}

View file

@ -0,0 +1,42 @@
use std::io::Write;
use anyhow::Result;
use crate::ast::{Stmt, Type};
use super::{type_to_llvm, LlvmIrCodegen};
impl<W: Write> LlvmIrCodegen<W> {
pub(super) fn convert_stmts(
&mut self,
stmts: impl AsRef<[Stmt<Type>]>,
) -> 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(())
}
}

9
src/codegen/mod.rs Normal file
View file

@ -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<Decl<Type>>) -> Result<()>;
}

View file

@ -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(())
}

View file

@ -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},
};
#[derive(Default)]
pub struct LayeredEnv<K, V> {
parent: Option<Box<LayeredEnv<K, V>>>,
inner_map: HashMap<K, V>,
}
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),
impl<K: Hash + Eq, V> LayeredEnv<K, V> {
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(),
}
}
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),
pub fn pop(self) -> LayeredEnv<K, V> {
*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,
},
}
}
}