From dfbdcd332aa5ba1bfc537be5d2569c4792b852de Mon Sep 17 00:00:00 2001 From: Per <Per Lindgren> Date: Thu, 3 Oct 2019 12:26:23 +0200 Subject: [PATCH] crust using inkwell --- examples/crust.rs | 530 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 530 insertions(+) create mode 100644 examples/crust.rs diff --git a/examples/crust.rs b/examples/crust.rs new file mode 100644 index 0000000..b9e8077 --- /dev/null +++ b/examples/crust.rs @@ -0,0 +1,530 @@ +use crust::{ + ast::{Expr, Func, Sig, Span}, + parse::parse_expr, +}; +use inkwell::{ + builder::Builder, + context::Context, + execution_engine::{ExecutionEngine, JitFunction}, + module::Module, + passes::PassManager, + types::BasicTypeEnum, + values::{BasicValueEnum, FloatValue, FunctionValue, PointerValue}, + FloatPredicate, OptimizationLevel, +}; +use std::collections::HashMap; + +use std::error::Error; + +type ExprFunc = unsafe extern "C" fn() -> i32; + +fn jit_compile_sum( + context: &Context, + module: &Module, + builder: &Builder, + execution_engine: &ExecutionEngine, +) -> Option<JitFunction<ExprFunc>> { + let u32_type = context.i32_type(); + let fn_type = u32_type.fn_type(&[], false); + + let function = module.add_function("expr", fn_type, None); + let basic_block = context.append_basic_block(&function, "entry"); + + builder.position_at_end(&basic_block); + + let x = context.i32_type().const_int(1, false); + let y = context.i32_type().const_int(2, false); + let z = context.i32_type().const_int(3, false); + + let sum = builder.build_int_add(x, y, "sum"); + let sum = builder.build_int_add(sum, z, "sum"); + + builder.build_return(Some(&sum)); + + unsafe { execution_engine.get_function("expr").ok() } +} + +fn main() -> Result<(), Box<dyn Error>> { + let context = Context::create(); + let module = context.create_module("expr"); + let builder = context.create_builder(); + let execution_engine = + module.create_jit_execution_engine(OptimizationLevel::None)?; + + let p = parse_expr(Span::new( + " + 1 + 2 + 3 + ", + )); + println!("{:?}", &p); + + let fun_expr = + jit_compile_sum(&context, &module, &builder, &execution_engine) + .ok_or("Unable to JIT compile `expr`")?; + + unsafe { + println!("{}", fun_expr.call()); + } + + Ok(()) +} + +// fn main() { +// let p = parse_prog(Span::new( +// " +// fn main() -> i32 +// { +// let a : i32 = 1 + 2; +// a = a + 1; +// return a +// } +// ", +// )); +// println!("{:?}", &p); +// } + +// // ====================================================================================== +// // COMPILER ============================================================================= +// // ====================================================================================== + +// /// Defines the `Expr` compiler. +// pub struct Compiler<'a> { +// pub context: &'a Context, +// pub builder: &'a Builder, +// pub fpm: &'a PassManager<FunctionValue>, +// pub module: &'a Module, +// pub function: &'a Func<'a>, + +// variables: HashMap<String, PointerValue>, +// fn_value_opt: Option<FunctionValue>, +// } + +// impl<'a> Compiler<'a> { +// /// Gets a defined function given its name. +// #[inline] +// fn get_function(&self, name: &str) -> Option<FunctionValue> { +// self.module.get_function(name) +// } + +// /// Returns the `FunctionValue` representing the function being compiled. +// #[inline] +// fn fn_value(&self) -> FunctionValue { +// self.fn_value_opt.unwrap() +// } + +// /// Creates a new stack allocation instruction in the entry block of the function. +// fn create_entry_block_alloca(&self, name: &str) -> PointerValue { +// let builder = self.context.create_builder(); + +// let entry = self.fn_value().get_first_basic_block().unwrap(); + +// match entry.get_first_instruction() { +// Some(first_instr) => builder.position_before(&first_instr), +// None => builder.position_at_end(&entry), +// } + +// builder.build_alloca(self.context.f64_type(), name) +// } + +// /// Compiles the specified `Expr` into an LLVM `FloatValue`. +// fn compile_expr( +// &mut self, +// expr: &Expr, +// ) -> Result<FloatValue, &'static str> { +// match *expr { +// // Expr::Num(nb) => Ok(self.context.i32_type().const_int(nb, true)), + +// // Expr::Variable(ref name) => { +// // match self.variables.get(name.as_str()) { +// // Some(var) => Ok(self +// // .builder +// // .build_load(*var, name.as_str()) +// // .into_float_value()), +// // None => Err("Could not find a matching variable."), +// // } +// // } + +// // Expr::VarIn { +// // ref variables, +// // ref body, +// // } => { +// // let mut old_bindings = Vec::new(); + +// // for &(ref var_name, ref initializer) in variables { +// // let var_name = var_name.as_str(); + +// // let initial_val = match *initializer { +// // Some(ref init) => self.compile_expr(init)?, +// // None => self.context.f64_type().const_float(0.), +// // }; + +// // let alloca = self.create_entry_block_alloca(var_name); + +// // self.builder.build_store(alloca, initial_val); + +// // if let Some(old_binding) = self.variables.remove(var_name) { +// // old_bindings.push(old_binding); +// // } + +// // self.variables.insert(var_name.to_string(), alloca); +// // } + +// // let body = self.compile_expr(body)?; + +// // for binding in old_bindings { +// // self.variables.insert( +// // binding.get_name().to_str().unwrap().to_string(), +// // binding, +// // ); +// // } + +// // Ok(body) +// // } + +// // Expr::Binary { +// // op, +// // ref left, +// // ref right, +// // } => { +// // if op == '=' { +// // // handle assignement +// // let var_name = match *left.borrow() { +// // Expr::Variable(ref var_name) => var_name, +// // _ => { +// // return Err("Expected variable as left-hand operator of assignement."); +// // } +// // }; + +// // let var_val = self.compile_expr(right)?; +// // let var = self +// // .variables +// // .get(var_name.as_str()) +// // .ok_or("Undefined variable.")?; + +// // self.builder.build_store(*var, var_val); + +// // Ok(var_val) +// // } else { +// // let lhs = self.compile_expr(left)?; +// // let rhs = self.compile_expr(right)?; + +// // match op { +// // '+' => { +// // Ok(self.builder.build_float_add(lhs, rhs, "tmpadd")) +// // } +// // '-' => { +// // Ok(self.builder.build_float_sub(lhs, rhs, "tmpsub")) +// // } +// // '*' => { +// // Ok(self.builder.build_float_mul(lhs, rhs, "tmpmul")) +// // } +// // '/' => { +// // Ok(self.builder.build_float_div(lhs, rhs, "tmpdiv")) +// // } +// // '<' => Ok({ +// // let cmp = self.builder.build_float_compare( +// // FloatPredicate::ULT, +// // lhs, +// // rhs, +// // "tmpcmp", +// // ); + +// // self.builder.build_unsigned_int_to_float( +// // cmp, +// // self.context.f64_type(), +// // "tmpbool", +// // ) +// // }), +// // '>' => Ok({ +// // let cmp = self.builder.build_float_compare( +// // FloatPredicate::ULT, +// // rhs, +// // lhs, +// // "tmpcmp", +// // ); + +// // self.builder.build_unsigned_int_to_float( +// // cmp, +// // self.context.f64_type(), +// // "tmpbool", +// // ) +// // }), + +// // custom => { +// // let mut name = String::from("binary"); + +// // name.push(custom); + +// // match self.get_function(name.as_str()) { +// // Some(fun) => { +// // match self +// // .builder +// // .build_call( +// // fun, +// // &[lhs.into(), rhs.into()], +// // "tmpbin", +// // ) +// // .try_as_basic_value() +// // .left() +// // { +// // Some(value) => { +// // Ok(value.into_float_value()) +// // } +// // None => Err("Invalid call produced."), +// // } +// // } + +// // None => Err("Undefined binary operator."), +// // } +// // } +// // } +// // } +// // } + +// // Expr::Call { +// // ref fn_name, +// // ref args, +// // } => match self.get_function(fn_name.as_str()) { +// // Some(fun) => { +// // let mut compiled_args = Vec::with_capacity(args.len()); + +// // for arg in args { +// // compiled_args.push(self.compile_expr(arg)?); +// // } + +// // let argsv: Vec<BasicValueEnum> = compiled_args +// // .iter() +// // .by_ref() +// // .map(|&val| val.into()) +// // .collect(); + +// // match self +// // .builder +// // .build_call(fun, argsv.as_slice(), "tmp") +// // .try_as_basic_value() +// // .left() +// // { +// // Some(value) => Ok(value.into_float_value()), +// // None => Err("Invalid call produced."), +// // } +// // } +// // None => Err("Unknown function."), +// // }, + +// // Expr::Conditional { +// // ref cond, +// // ref consequence, +// // ref alternative, +// // } => { +// // let parent = self.fn_value(); +// // let zero_const = self.context.f64_type().const_float(0.0); + +// // // create condition by comparing without 0.0 and returning an int +// // let cond = self.compile_expr(cond)?; +// // let cond = self.builder.build_float_compare( +// // FloatPredicate::ONE, +// // cond, +// // zero_const, +// // "ifcond", +// // ); + +// // // build branch +// // let then_bb = self.context.append_basic_block(&parent, "then"); +// // let else_bb = self.context.append_basic_block(&parent, "else"); +// // let cont_bb = +// // self.context.append_basic_block(&parent, "ifcont"); + +// // self.builder +// // .build_conditional_branch(cond, &then_bb, &else_bb); + +// // // build then block +// // self.builder.position_at_end(&then_bb); +// // let then_val = self.compile_expr(consequence)?; +// // self.builder.build_unconditional_branch(&cont_bb); + +// // let then_bb = self.builder.get_insert_block().unwrap(); + +// // // build else block +// // self.builder.position_at_end(&else_bb); +// // let else_val = self.compile_expr(alternative)?; +// // self.builder.build_unconditional_branch(&cont_bb); + +// // let else_bb = self.builder.get_insert_block().unwrap(); + +// // // emit merge block +// // self.builder.position_at_end(&cont_bb); + +// // let phi = +// // self.builder.build_phi(self.context.f64_type(), "iftmp"); + +// // phi.add_incoming(&[ +// // (&then_val, &then_bb), +// // (&else_val, &else_bb), +// // ]); + +// // Ok(phi.as_basic_value().into_float_value()) +// // } + +// // Expr::For { +// // ref var_name, +// // ref start, +// // ref end, +// // ref step, +// // ref body, +// // } => { +// // let parent = self.fn_value(); + +// // let start_alloca = self.create_entry_block_alloca(var_name); +// // let start = self.compile_expr(start)?; + +// // self.builder.build_store(start_alloca, start); + +// // // go from current block to loop block +// // let loop_bb = self.context.append_basic_block(&parent, "loop"); + +// // self.builder.build_unconditional_branch(&loop_bb); +// // self.builder.position_at_end(&loop_bb); + +// // let old_val = self.variables.remove(var_name.as_str()); + +// // self.variables.insert(var_name.to_owned(), start_alloca); + +// // // emit body +// // self.compile_expr(body)?; + +// // // emit step +// // let step = match *step { +// // Some(ref step) => self.compile_expr(step)?, +// // None => self.context.f64_type().const_float(1.0), +// // }; + +// // // compile end condition +// // let end_cond = self.compile_expr(end)?; + +// // let curr_var = self.builder.build_load(start_alloca, var_name); +// // let next_var = self.builder.build_float_add( +// // curr_var.into_float_value(), +// // step, +// // "nextvar", +// // ); + +// // self.builder.build_store(start_alloca, next_var); + +// // let end_cond = self.builder.build_float_compare( +// // FloatPredicate::ONE, +// // end_cond, +// // self.context.f64_type().const_float(0.0), +// // "loopcond", +// // ); +// // let after_bb = +// // self.context.append_basic_block(&parent, "afterloop"); + +// // self.builder +// // .build_conditional_branch(end_cond, &loop_bb, &after_bb); +// // self.builder.position_at_end(&after_bb); + +// // self.variables.remove(var_name); + +// // if let Some(val) = old_val { +// // self.variables.insert(var_name.to_owned(), val); +// // } + +// // Ok(self.context.f64_type().const_float(0.0)) +// // } +// _ => unimplemented!(), +// } +// } + +// /// Compiles the specified `Prototype` into an extern LLVM `FunctionValue`. +// fn compile_prototype( +// &self, +// sig: &Sig, +// ) -> Result<FunctionValue, &'static str> { +// let ret_type = sig.2; +// let args_types = std::iter::repeat(ret_type) +// .take(sig.1.len()) +// .map(|f| f.into()) +// .collect::<Vec<BasicTypeEnum>>(); +// let args_types = args_types.as_slice(); + +// let fn_type = self.context.f64_type().fn_type(args_types, false); +// let fn_val = self.module.add_function(sig.0.as_str(), fn_type, None); + +// // set arguments names +// for (i, arg) in fn_val.get_param_iter().enumerate() { +// arg.into_float_value().set_name(proto.args[i].as_str()); +// } + +// // finally return built prototype +// Ok(fn_val) +// } + +// /// Compiles the specified `Function` into an LLVM `FunctionValue`. +// fn compile_fn(&mut self) -> Result<FunctionValue, &'static str> { +// let sig = &self.function.sig; +// let function = self.compile_sig(sig)?; + +// // got external function, returning only compiled prototype +// // if self.function.body.is_none() { +// // return Ok(function); +// // } + +// let entry = self.context.append_basic_block(&function, "entry"); + +// self.builder.position_at_end(&entry); + +// // update fn field +// self.fn_value_opt = Some(function); + +// // build variables map +// self.variables.reserve(proto.args.len()); + +// for (i, arg) in function.get_param_iter().enumerate() { +// let arg_name = proto.args[i].as_str(); +// let alloca = self.create_entry_block_alloca(arg_name); + +// self.builder.build_store(alloca, arg); + +// self.variables.insert(proto.args[i].clone(), alloca); +// } + +// // compile body +// let body = self.compile_expr(self.function.body.as_ref().unwrap())?; + +// self.builder.build_return(Some(&body)); + +// // return the whole thing after verification and optimization +// if function.verify(true) { +// self.fpm.run_on(&function); + +// Ok(function) +// } else { +// unsafe { +// function.delete(); +// } + +// Err("Invalid generated function.") +// } +// } + +// /// Compiles the specified `Function` in the given `Context` and using the specified `Builder`, `PassManager`, and `Module`. +// pub fn compile( +// context: &'a Context, +// builder: &'a Builder, +// pass_manager: &'a PassManager<FunctionValue>, +// module: &'a Module, +// function: &Function, +// ) -> Result<FunctionValue, &'static str> { +// let mut compiler = Compiler { +// context: context, +// builder: builder, +// fpm: pass_manager, +// module: module, +// function: function, +// fn_value_opt: None, +// variables: HashMap::new(), +// }; + +// compiler.compile_fn() +// } +// } -- GitLab