diff --git a/src/backend/error.rs b/src/backend/error.rs index f7252e9..ad32cff 100644 --- a/src/backend/error.rs +++ b/src/backend/error.rs @@ -43,6 +43,11 @@ pub enum BackendError { InvalidTypeCast { from: PrimitiveType, to: Type }, #[error("Unknown string constant '{0}")] UnknownString(ArcIntern), + #[error("Compiler doesn't currently support function arguments")] + NoFunctionArguments { + function_name: String, + argument_name: String, + }, } impl From for Diagnostic { @@ -73,6 +78,13 @@ impl From for Diagnostic { ), BackendError::UnknownString(str) => Diagnostic::error() .with_message(format!("Unknown string found trying to compile: '{}'", str)), + BackendError::NoFunctionArguments { + function_name, + argument_name, + } => Diagnostic::error().with_message(format!( + "Function {} takes a function argument ({}), which is not supported", + function_name, argument_name + )), } } } @@ -128,6 +140,17 @@ impl PartialEq for BackendError { BackendError::UnknownString(b) => a == b, _ => false, }, + + BackendError::NoFunctionArguments { + function_name: f1, + argument_name: a1, + } => match other { + BackendError::NoFunctionArguments { + function_name: f2, + argument_name: a2, + } => f1 == f2 && a1 == a2, + _ => false, + }, } } } diff --git a/src/backend/eval.rs b/src/backend/eval.rs index 7768ef8..e3a77eb 100644 --- a/src/backend/eval.rs +++ b/src/backend/eval.rs @@ -206,15 +206,15 @@ proptest::proptest! { #[test] fn jit_backend(program in Program::arbitrary()) { use crate::eval::PrimOpError; -// use pretty::{DocAllocator, Pretty}; -// let allocator = pretty::BoxAllocator; -// allocator -// .text("---------------") -// .append(allocator.hardline()) -// .append(program.pretty(&allocator)) -// .1 -// .render_colored(70, pretty::termcolor::StandardStream::stdout(pretty::termcolor::ColorChoice::Auto)) -// .expect("rendering works"); + use pretty::{DocAllocator, Pretty}; + let allocator = pretty::BoxAllocator; + allocator + .text("---------------") + .append(allocator.hardline()) + .append(program.pretty(&allocator)) + .1 + .render_colored(70, pretty::termcolor::StandardStream::stdout(pretty::termcolor::ColorChoice::Auto)) + .expect("rendering works"); let basic_result = program.eval().map(|(_,x)| x); diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index 960cd55..7fb11fe 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -21,6 +21,7 @@ use crate::backend::Backend; enum ReferenceBuilder { Global(ConstantType, GlobalValue), Local(ConstantType, cranelift_frontend::Variable), + Argument(ConstantType, entities::Value), } impl ReferenceBuilder { @@ -36,6 +37,8 @@ impl ReferenceBuilder { let value = builder.use_var(*var); (value, *ty) } + + ReferenceBuilder::Argument(ty, val) => (*val, *ty), } } } @@ -172,6 +175,21 @@ impl Backend { // flow, we might add more blocks after this one. But, for now, we only have // the one block. let main_block = builder.create_block(); + // add the block parameters, which should be the function parameters + for (name, ty) in arguments.iter() { + let constant_type = ty + .try_into() + .map_err(|_| BackendError::NoFunctionArguments { + function_name: function_name.to_string(), + argument_name: name.to_string(), + })?; + let value = builder.append_block_param(main_block, ir::Type::from(constant_type)); + variables.insert( + name.clone(), + ReferenceBuilder::Argument(constant_type, value), + ); + } + builder.switch_to_block(main_block); let (value, _) = self.compile_expression(body, &mut variables, &mut builder)?; @@ -388,14 +406,23 @@ impl Backend { match valref { ValueOrRef::Value(_, _, val) => match val { Value::I8(_, v) => { - Ok((builder.ins().iconst(types::I8, v as i64), ConstantType::I8)) + // Cranelift does a funny thing where it checks you aren't using bits in the I64 + // we provide above the size of the type we provide. So, in this case, we can only + // set the low 8 bits of the i64. This restriction creates a bit of a problem when + // casting direction from i8 to i64, because Rust will (helpfully) sign-extend the + // negative number for us. Which sets the high bits, which makes Cranelift unhappy. + // So first we cast the i8 as u8, to get rid of the whole concept of sign extension, + // and *then* we cast to i64. + Ok((builder.ins().iconst(types::I8, v as u8 as i64), ConstantType::I8)) } Value::I16(_, v) => Ok(( - builder.ins().iconst(types::I16, v as i64), + // see above note for the "... as ... as" + builder.ins().iconst(types::I16, v as u16 as i64), ConstantType::I16, )), Value::I32(_, v) => Ok(( - builder.ins().iconst(types::I32, v as i64), + // see above note for the "... as ... as" + builder.ins().iconst(types::I32, v as u32 as i64), ConstantType::I32, )), Value::I64(_, v) => Ok((builder.ins().iconst(types::I64, v), ConstantType::I64)), diff --git a/src/bin/ngrun.rs b/src/bin/ngrun.rs index 2296a06..1d5ca43 100644 --- a/src/bin/ngrun.rs +++ b/src/bin/ngrun.rs @@ -33,19 +33,37 @@ fn print_result(result: (Value, String)) { println!("RESULT: {}", result.0); } -fn main() -> Result<(), anyhow::Error> { +fn jit(ir: ngr::ir::Program) -> Result { + let mut backend = Backend::jit(None)?; + let function_id = backend.compile_program("gogogo", ir)?; + backend.module.finalize_definitions()?; + let compiled_bytes = backend.bytes(function_id); + Ok(unsafe { std::mem::transmute::<_, fn() -> ()>(compiled_bytes) }) +} + +fn main() { let cli = CommandLineArguments::parse(); let mut file_database = SimpleFiles::new(); let mut console = StandardStream::stdout(pretty::termcolor::ColorChoice::Auto); let console_options = codespan_reporting::term::Config::default(); - let syntax = syntax::Program::parse_file(&mut file_database, cli.file.as_ref())?; + let syntax = syntax::Program::parse_file(&mut file_database, cli.file.as_ref()); let mut emit = |x| { let _ = codespan_reporting::term::emit(&mut console, &console_options, &file_database, &x); }; + let syntax = match syntax { + Ok(x) => x, + Err(e) => { + emit((&e).into()); + return; + } + }; if cli.interpreter == Interpreter::Syntax { - print_result(syntax.eval()?); - return Ok(()); + match syntax.eval() { + Err(e) => println!("Evaluation error: {}", e), + Ok(v) => print_result(v), + } + return; } let ir = match syntax.type_infer() { @@ -62,21 +80,20 @@ fn main() -> Result<(), anyhow::Error> { for error in errors { emit(error.into()); } - return Err(anyhow::anyhow!("failed to infer program types")); + return; } }; if cli.interpreter == Interpreter::IR { - print_result(ir.eval()?); - return Ok(()); + match ir.eval() { + Err(e) => println!("Evaluation error: {}", e), + Ok(v) => print_result(v), + } + return; } - let mut backend = Backend::jit(None)?; - let function_id = backend.compile_program("gogogo", ir)?; - backend.module.finalize_definitions()?; - let compiled_bytes = backend.bytes(function_id); - let compiled_function = unsafe { std::mem::transmute::<_, fn() -> ()>(compiled_bytes) }; - compiled_function(); - - Ok(()) + match jit(ir) { + Err(e) => emit(e.into()), + Ok(compiled_function) => compiled_function(), + } } diff --git a/src/eval/primop.rs b/src/eval/primop.rs index 464562b..322459f 100644 --- a/src/eval/primop.rs +++ b/src/eval/primop.rs @@ -191,7 +191,7 @@ impl Value { left.clone(), right.clone(), )), - } + }, Value::Closure(_, _, _, _) | Value::Void => { Err(PrimOpError::BadTypeFor(operation.to_string(), left.clone())) } diff --git a/src/eval/primtype.rs b/src/eval/primtype.rs index dc42d1b..0624ba2 100644 --- a/src/eval/primtype.rs +++ b/src/eval/primtype.rs @@ -78,6 +78,22 @@ impl From for PrimitiveType { } } +impl From for ConstantType { + fn from(value: PrimitiveType) -> Self { + match value { + PrimitiveType::Void => ConstantType::Void, + PrimitiveType::I8 => ConstantType::I8, + PrimitiveType::I16 => ConstantType::I16, + PrimitiveType::I32 => ConstantType::I32, + PrimitiveType::I64 => ConstantType::I64, + PrimitiveType::U8 => ConstantType::U8, + PrimitiveType::U16 => ConstantType::U16, + PrimitiveType::U32 => ConstantType::U32, + PrimitiveType::U64 => ConstantType::U64, + } + } +} + #[derive(thiserror::Error, Debug, Clone, PartialEq)] pub enum UnknownPrimType { #[error("Could not convert '{0}' into a primitive type")] diff --git a/src/eval/value.rs b/src/eval/value.rs index a49319b..e3b8230 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -130,7 +130,7 @@ impl PartialEq> for Value { Value::U64(y) => x == y, Value::Number(y) => x == y, _ => false, - } + }, Value::Closure(_, _, _, _) => false, } } diff --git a/src/ir/arbitrary.rs b/src/ir/arbitrary.rs index bf531a5..47aed0b 100644 --- a/src/ir/arbitrary.rs +++ b/src/ir/arbitrary.rs @@ -226,11 +226,11 @@ impl ValueTree for ProgramTree { } fn simplify(&mut self) -> bool { - unimplemented!() + false } fn complicate(&mut self) -> bool { - unimplemented!() + false } } diff --git a/src/ir/ast.rs b/src/ir/ast.rs index 62737fd..b3d83d3 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -466,6 +466,17 @@ impl From for Type { } } +impl<'a> TryInto for &'a Type { + type Error = (); + + fn try_into(self) -> Result { + match self { + Type::Primitive(pt) => Ok((*pt).into()), + Type::Function(_, _) => Err(()), + } + } +} + #[derive(Clone, Debug, Eq, PartialEq)] pub enum TypeOrVar { Primitive(PrimitiveType),