diff --git a/.gitignore b/.gitignore index 96ef6c0..c48cd87 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target Cargo.lock +**/*.o \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index c881f33..cb15903 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,11 @@ path = "src/bin.rs" clap = { version = "^3.0.14", features = ["derive"] } codespan = "0.11.1" codespan-reporting = "0.11.1" +cranelift-codegen = "^0.89.2" +cranelift-frontend = "^0.89.2" +cranelift-module = "^0.89.2" +cranelift-native = "^0.89.2" +cranelift-object = "^0.89.2" lalrpop-util = "^0.19.7" lazy_static = "^1.4.0" logos = "^0.12.0" diff --git a/runtime/rts.c b/runtime/rts.c new file mode 100644 index 0000000..51b21a7 --- /dev/null +++ b/runtime/rts.c @@ -0,0 +1,17 @@ +#include +#include + +void print(char *variable_name, uint64_t value) { + printf("%s = %lu", variable_name, value); +} + +void caller() { + print("x", 4); +} + +extern void gogogo(); + +int main(int argc, char **argv) { + gogogo(); + return 0; +} \ No newline at end of file diff --git a/src/asts/lil.rs b/src/asts/lil.rs index c2bf641..bb2b6fd 100644 --- a/src/asts/lil.rs +++ b/src/asts/lil.rs @@ -4,6 +4,7 @@ use pretty::{DocAllocator, DocBuilder, Pretty}; pub struct Program { pub statements: Vec>, + pub variable_info: VariableMap, } impl Program { diff --git a/src/backend.rs b/src/backend.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/bin.rs b/src/bin.rs index 2abe403..ae5d6e9 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -3,9 +3,16 @@ use codespan_reporting::diagnostic::Diagnostic; use codespan_reporting::files::SimpleFiles; use codespan_reporting::term; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; +use cranelift_codegen::ir::{AbiParam, types, Signature}; +use cranelift_codegen::isa::CallConv; +use cranelift_codegen::{isa, settings, CodegenError}; +use cranelift_module::{default_libcall_names, ModuleError, Linkage, Module}; +use cranelift_object::{ObjectBuilder, ObjectModule, object}; use ngr::asts::lil; use ngr::passes::run_front_end; use pretty::Arena; +use std::io; +use thiserror::Error; #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] @@ -18,7 +25,25 @@ struct CommandLineArguments { file: String, } -fn main() { +#[derive(Debug, Error)] +enum MainError { + #[error("Error parsing triple: {0}")] + Isa(#[from] isa::LookupError), + + #[error("Code generation error: {0}")] + Codegen(#[from] CodegenError), + + #[error("Module error: {0}")] + Module(#[from] ModuleError), + + #[error("IO error: {0}")] + IO(#[from] io::Error), + + #[error("Object write error: {0}")] + Object(#[from] object::write::Error), +} + +fn main() -> Result<(), MainError> { let args = CommandLineArguments::parse(); let mut file_database = SimpleFiles::new(); let initial_file_name = &args.file; @@ -46,13 +71,35 @@ fn main() { .unwrap(); } - if let Some((hil_tree, mut variable_map)) = hil_conversion_result.result { - let arena = Arena::new(); - let lil_tree = lil::Program::convert(hil_tree, &mut variable_map); - lil_tree - .pretty(&variable_map, &arena) - .into_doc() - .render_colored(72, StandardStream::stdout(ColorChoice::Auto)) - .unwrap() + if let Some((hil_tree, variable_map)) = hil_conversion_result.result { + //let arena = Arena::new(); + let lil_tree = lil::Program::convert(hil_tree, variable_map.clone()); + + let isa = isa::lookup_by_name("aarch64-apple-darwin")?.finish(settings::Flags::new(settings::builder()))?; + let object_builder = ObjectBuilder::new(isa, "example", default_libcall_names())?; + let mut object_module = ObjectModule::new(object_builder); + + let print_signature = Signature { + params: vec![AbiParam::new(types::I64)], + returns: vec![], + call_conv: CallConv::SystemV, + }; + let print_func_id = object_module.declare_function("print", Linkage::Import, &print_signature)?; + + let _compiled = lil_tree.into_cranelift(&mut object_module, print_func_id)?; + + // something? + + let bytes = object_module.finish().emit()?; + + std::fs::write("output.o", bytes)?; + +// lil_tree +// .pretty(&variable_map, &arena) +// .into_doc() +// .render_colored(72, StandardStream::stdout(ColorChoice::Auto)) +// .unwrap() } + + Ok(()) } diff --git a/src/errors.rs b/src/errors.rs index c80a1f0..3c51c78 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -21,6 +21,9 @@ pub enum Error { #[error("Unbound variable '{0}'")] UnboundVariable(Location, String), + + #[error("Internal error: {0}")] + InternalError(Location, String), } fn locations_to_labels(start: &Location, end: &Location) -> Vec> { @@ -168,6 +171,17 @@ impl From for Diagnostic { ]) .with_message(format!("Unbound variable '{}'", name)), }, + + Error::InternalError(location, string) => match location { + Location::Manufactured => { + Diagnostic::error().with_message(format!("Internal error: {}", string)) + } + Location::InFile(file_id, offset) => Diagnostic::error() + .with_labels(vec![ + Label::primary(*file_id, *offset..*offset).with_message("this is related") + ]) + .with_message(format!("Internal error: {}", string)), + } } } } diff --git a/src/lib.rs b/src/lib.rs index 1936d8f..1bdb953 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ pub mod asts; +pub mod backend; pub mod errors; pub mod passes; pub mod syntax; diff --git a/src/passes.rs b/src/passes.rs index 2846d5d..299babd 100644 --- a/src/passes.rs +++ b/src/passes.rs @@ -9,6 +9,7 @@ use std::fs; mod hil_to_lil; mod syntax_to_hil; +mod into_crane; pub struct PassResult { pub result: T, diff --git a/src/passes/hil_to_lil.rs b/src/passes/hil_to_lil.rs index 310b7dc..e2d7cbe 100644 --- a/src/passes/hil_to_lil.rs +++ b/src/passes/hil_to_lil.rs @@ -3,14 +3,20 @@ use crate::asts::lil; use crate::variable_map::VariableMap; impl lil::Program { - pub fn convert(hil_program: hil::Program, variable_map: &mut VariableMap) -> Self { + pub fn convert(hil_program: hil::Program, mut variable_map: VariableMap) -> Self { let mut statements = Vec::new(); for hil_statement in hil_program.statements { - statements.append(&mut lil::Statement::convert(hil_statement, variable_map)); + statements.append(&mut lil::Statement::convert( + hil_statement, + &mut variable_map, + )); } - lil::Program { statements } + lil::Program { + statements, + variable_info: variable_map, + } } } diff --git a/src/passes/into_crane.rs b/src/passes/into_crane.rs new file mode 100644 index 0000000..4399ba5 --- /dev/null +++ b/src/passes/into_crane.rs @@ -0,0 +1,95 @@ +use crate::asts::lil::{Program, Statement, Primitive, SimpleExpression, Value}; +use cranelift_codegen::entity::EntityRef; +use cranelift_codegen::ir::{Function, UserFuncName, UserExternalName, Signature, types, InstBuilder, entities, ExternalName, ExtFuncData, AbiParam}; +use cranelift_codegen::isa::CallConv; +use cranelift_codegen::{Context}; +use cranelift_frontend::{FunctionBuilderContext, FunctionBuilder, Variable}; +use cranelift_module::{FuncId, Module, Linkage, ModuleError, ModuleCompiledFunction}; + +impl Program { + + pub fn into_cranelift(&self, module: &mut M, print_func_id: FuncId) -> Result { + let basic_signature = Signature { + params: vec![], + returns: vec![], + call_conv: CallConv::SystemV, + }; + + let func_id = module.declare_function("gogogo", Linkage::Export, &basic_signature)?; + let mut ctx = Context::new(); + ctx.func = Function::with_name_signature(UserFuncName::user(0, func_id.as_u32()), basic_signature); + + let print_func_ref = module.declare_func_in_func(print_func_id, &mut ctx.func); + + let mut fctx = FunctionBuilderContext::new(); + let mut builder = FunctionBuilder::new(&mut ctx.func, &mut fctx); + let main_block = builder.create_block(); + builder.switch_to_block(main_block); + + for stmt in self.statements.iter() { + match stmt { + Statement::Print(ann, var) => { + let var = builder.use_var(Variable::new(*var)); + builder.ins().call(print_func_ref, &[var]); + } + + Statement::ResultBinding(_, varnum, value) => { + let var = Variable::new(*varnum); + builder.declare_var(var, types::I64); + + let val = match value { + Primitive::Plus(left, right) => { + let left = left.into_cranelift(&mut builder); + let right = right.into_cranelift(&mut builder); + builder.ins().iadd(left, right) + } + + Primitive::Minus(left, right) => { + let left = left.into_cranelift(&mut builder); + let right = right.into_cranelift(&mut builder); + builder.ins().isub(left, right) + } + + Primitive::Times(left, right) => { + let left = left.into_cranelift(&mut builder); + let right = right.into_cranelift(&mut builder); + builder.ins().imul(left, right) + } + + Primitive::Divide(left, right) => { + let left = left.into_cranelift(&mut builder); + let right = right.into_cranelift(&mut builder); + builder.ins().sdiv(left, right) + } + }; + builder.def_var(var, val); + } + + Statement::VariableBinding(_, varnum, exp) => { + let var = Variable::new(*varnum); + builder.declare_var(var, types::I64); + let val = exp.into_cranelift(&mut builder); + builder.def_var(var, val); + } + } + } + + builder.ins().return_(&[]); + builder.seal_block(main_block); + builder.finalize(); + + module.define_function(func_id, &mut ctx) + } +} + +impl SimpleExpression { + fn into_cranelift(&self, builder: &mut FunctionBuilder) -> entities::Value { + match self { + SimpleExpression::Constant(_, value) => match value { + Value::Number(_base, numval) => builder.ins().iconst(types::I64, *numval as i64), + } + + SimpleExpression::Reference(_, num) => builder.use_var(Variable::new(*num)), + } + } +} \ No newline at end of file diff --git a/src/variable_map.rs b/src/variable_map.rs index f3177f2..5c7f33c 100644 --- a/src/variable_map.rs +++ b/src/variable_map.rs @@ -2,11 +2,14 @@ use crate::syntax::Location; use std::collections::HashMap; pub type Variable = usize; + +#[derive(Clone)] pub struct VariableMap { map: HashMap, next_index: Variable, } +#[derive(Clone)] struct VariableInfo { name: String, binding_location: Location,