use std::path::Path; use crate::backend::Backend; use crate::eval::EvalError; use crate::ir::Program; use cranelift_jit::JITModule; use cranelift_object::ObjectModule; use target_lexicon::Triple; impl Backend { pub fn eval(program: Program) -> Result { let mut jitter = Backend::jit(Some(String::new()))?; let function_id = jitter.compile_function("test", program)?; jitter.module.finalize_definitions()?; let compiled_bytes = jitter.bytes(function_id); let compiled_function = unsafe { std::mem::transmute::<_, fn() -> ()>(compiled_bytes) }; compiled_function(); Ok(jitter.output()) } } impl Backend { pub fn eval(program: Program) -> Result { //use pretty::{Arena, Pretty}; //let allocator = Arena::<()>::new(); //program.pretty(&allocator).render(80, &mut std::io::stdout())?; let mut backend = Self::object_file(Triple::host())?; let my_directory = tempfile::tempdir()?; let object_path = my_directory.path().join("object.o"); let executable_path = my_directory.path().join("test_executable"); backend.compile_function("gogogo", program)?; let bytes = backend.bytes()?; std::fs::write(&object_path, bytes)?; Self::link(&object_path, &executable_path)?; let output = std::process::Command::new(executable_path).output()?; if output.stderr.is_empty() { if output.status.success() { Ok(std::string::String::from_utf8_lossy(&output.stdout).to_string()) } else { Err(EvalError::IO(format!( "Exitted with error code {}", output.status ))) } } else { Err(EvalError::IO( std::string::String::from_utf8_lossy(&output.stderr).to_string(), )) } } fn link(object_file: &Path, executable_path: &Path) -> Result<(), EvalError> { use std::path::PathBuf; let output = std::process::Command::new("clang") .arg( PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("runtime") .join("rts.c"), ) .arg(object_file) .arg("-o") .arg(executable_path) .output()?; if !output.stderr.is_empty() { return Err(EvalError::IO( std::string::String::from_utf8_lossy(&output.stderr).to_string(), )); } Ok(()) } } proptest::proptest! { #[test] fn file_backend_works(program: Program) { use crate::eval::PrimOpError; let basic_result = program.eval(); #[cfg(target_family="windows")] let basic_result = basic_result.map(|x| x.replace('\n', "\r\n")); if !matches!(basic_result, Err(EvalError::PrimOp(PrimOpError::MathFailure(_)))) { let compiled_result = Backend::::eval(program); assert_eq!(basic_result, compiled_result); } } #[test] fn jit_backend_works(program: Program) { use crate::eval::PrimOpError; let basic_result = program.eval(); if !matches!(basic_result, Err(EvalError::PrimOp(PrimOpError::MathFailure(_)))) { let compiled_result = Backend::::eval(program); assert_eq!(basic_result, compiled_result); } } }