diff --git a/diffsl/src/execution/cranelift/codegen.rs b/diffsl/src/execution/cranelift/codegen.rs index 6bcaafa..36fc22d 100644 --- a/diffsl/src/execution/cranelift/codegen.rs +++ b/diffsl/src/execution/cranelift/codegen.rs @@ -31,6 +31,8 @@ pub struct CraneliftModule { /// The module, with the object backend. module: Mutex, + emitted_object: Mutex>>, + layout: DataLayout, indices_id: DataId, @@ -38,7 +40,7 @@ pub struct CraneliftModule { model_index_id: DataId, thread_counter: Option, - //triple: Triple, + triple: Triple, int_type: types::Type, real_type: types::Type, real_ptr_type: types::Type, @@ -575,9 +577,11 @@ impl CraneliftModule { builder_context: FunctionBuilderContext::new(), ctx: module.make_context(), module: Mutex::new(module), + emitted_object: Mutex::new(None), indices_id, constants_id, model_index_id, + triple, int_type, real_type: real_type_cranelift, real_ptr_type: ptr_type, @@ -1094,6 +1098,20 @@ impl CraneliftModule { } } +impl CraneliftModule { + fn new_object_backend(triple: Triple) -> Result { + let mut flag_builder = settings::builder(); + flag_builder.set("use_colocated_libcalls", "false").unwrap(); + flag_builder.set("is_pic", "false").unwrap(); + flag_builder.set("opt_level", "speed").unwrap(); + let flags = settings::Flags::new(flag_builder); + let isa = isa::lookup(triple)?.finish(flags)?; + let builder = + ObjectBuilder::new(isa, "diffsol", cranelift_module::default_libcall_names())?; + Ok(ObjectModule::new(builder)) + } +} + impl CodegenModule for CraneliftModule {} impl CodegenModuleCompile for CraneliftModule { @@ -1108,16 +1126,7 @@ impl CodegenModuleCompile for CraneliftModule { let threaded = thread_dim > 1; let triple = triple.unwrap_or(Triple::host()); - let mut flag_builder = settings::builder(); - flag_builder.set("use_colocated_libcalls", "false").unwrap(); - flag_builder.set("is_pic", "false").unwrap(); - flag_builder.set("opt_level", "speed").unwrap(); - let flags = settings::Flags::new(flag_builder); - let isa = isa::lookup(triple.clone())?.finish(flags)?; - let builder = - ObjectBuilder::new(isa, "diffsol", cranelift_module::default_libcall_names())?; - - let module = ObjectModule::new(builder); + let module = Self::new_object_backend(triple.clone())?; Self::new(triple, model, threaded, module, real_type) } @@ -1192,9 +1201,18 @@ impl CodegenModuleCompile for CraneliftModule { } impl CodegenModuleEmit for CraneliftModule { - fn to_object(self) -> Result> { - let module = Mutex::into_inner(self.module).unwrap(); - module.finish().emit().map_err(|e| anyhow!(e)) + fn to_object(&self) -> Result> { + let mut emitted_object = self.emitted_object.lock().unwrap(); + if let Some(buffer) = emitted_object.as_ref() { + return Ok(buffer.clone()); + } + + let mut module = self.module.lock().unwrap(); + let module_to_emit = + std::mem::replace(&mut *module, Self::new_object_backend(self.triple.clone())?); + let buffer = module_to_emit.finish().emit().map_err(|e| anyhow!(e))?; + *emitted_object = Some(buffer.clone()); + Ok(buffer) } } diff --git a/diffsl/src/execution/llvm/codegen.rs b/diffsl/src/execution/llvm/codegen.rs index 88d0a52..98deed4 100644 --- a/diffsl/src/execution/llvm/codegen.rs +++ b/diffsl/src/execution/llvm/codegen.rs @@ -716,7 +716,7 @@ impl CodegenModuleCompile for LlvmModule { } impl CodegenModuleEmit for LlvmModule { - fn to_object(self) -> Result> { + fn to_object(&self) -> Result> { let module = self.codegen().module(); //module.print_to_stderr(); let buffer = self diff --git a/diffsl/src/execution/module.rs b/diffsl/src/execution/module.rs index bb3501c..fc12368 100644 --- a/diffsl/src/execution/module.rs +++ b/diffsl/src/execution/module.rs @@ -29,5 +29,5 @@ pub trait CodegenModuleJit: CodegenModule { } pub trait CodegenModuleEmit: CodegenModule { - fn to_object(self) -> Result>; + fn to_object(&self) -> Result>; } diff --git a/diffsl/tests/roundtrips.rs b/diffsl/tests/roundtrips.rs index 21cee51..47f613b 100644 --- a/diffsl/tests/roundtrips.rs +++ b/diffsl/tests/roundtrips.rs @@ -196,3 +196,50 @@ fn cranelift_module_object_file_roundtrip() { assert_rhs_works(&compiler); let _ = std::fs::remove_file(object_path); } + +#[cfg(all( + feature = "cranelift", + not(target_arch = "wasm32"), + not(target_os = "macos") +))] +#[test] +fn cranelift_multiple_to_object_calls_roundtrip() { + use diffsl::discretise::DiscreteModel; + use diffsl::execution::compiler::{CompilerMode, CompilerOptions}; + use diffsl::execution::module::{CodegenModuleCompile, CodegenModuleEmit}; + use diffsl::execution::scalar::RealType; + use diffsl::parser::parse_ds_string; + use diffsl::{Compiler, CraneliftObjectModule, ObjectModule}; + + let code = model_code(); + let ast = parse_ds_string(code).expect("dsl should parse"); + let model = DiscreteModel::build("cranelift_multiple_object_roundtrip", &ast) + .expect("discrete model should build"); + + let cranelift_module = ::from_discrete_model( + &model, + CompilerOptions::default(), + None, + RealType::F64, + Some(code), + ) + .expect("cranelift module should compile"); + + let object_buffer_1 = cranelift_module + .to_object() + .expect("first object emission should succeed"); + let object_buffer_2 = cranelift_module + .to_object() + .expect("second object emission should succeed"); + + assert!(!object_buffer_1.is_empty()); + assert_eq!(object_buffer_1, object_buffer_2); + + let compiler = Compiler::::from_object_file( + object_buffer_2, + CompilerMode::SingleThreaded, + ) + .expect("compiler should build from object file"); + + assert_rhs_works(&compiler); +}