diff --git a/Cargo.lock b/Cargo.lock index d81e72149..dedccb96d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,6 +44,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cobs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" + [[package]] name = "cranelift-bforest" version = "0.114.0" @@ -58,6 +64,10 @@ name = "cranelift-bitset" version = "0.114.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "005884e3649c3e5ff2dc79e8a94b138f11569cc08a91244a292714d2a86e9156" +dependencies = [ + "serde", + "serde_derive", +] [[package]] name = "cranelift-codegen" @@ -79,6 +89,7 @@ dependencies = [ "regalloc2", "rustc-hash", "serde", + "serde_derive", "smallvec", "target-lexicon", ] @@ -114,6 +125,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "305399fd781a2953ac78c1396f02ff53144f39c33eb7fc7789cf4e8936d13a96" dependencies = [ "cranelift-bitset", + "serde", + "serde_derive", ] [[package]] @@ -163,6 +176,8 @@ dependencies = [ "anyhow", "cranelift-codegen", "cranelift-control", + "serde", + "serde_derive", ] [[package]] @@ -200,6 +215,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + [[package]] name = "equivalent" version = "1.0.1" @@ -297,6 +318,17 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "postcard" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8" +dependencies = [ + "cobs", + "embedded-io", + "serde", +] + [[package]] name = "proc-macro2" version = "1.0.86" @@ -324,6 +356,7 @@ dependencies = [ "hashbrown", "log", "rustc-hash", + "serde", "slice-group-by", "smallvec", ] @@ -360,6 +393,8 @@ dependencies = [ "indexmap", "libloading", "object", + "postcard", + "serde", "smallvec", "target-lexicon", ] @@ -395,6 +430,9 @@ name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] [[package]] name = "stable_deref_trait" diff --git a/Cargo.toml b/Cargo.toml index b2fed3c49..f48d41961 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,11 +16,13 @@ cranelift-jit = { version = "0.114.0", optional = true } cranelift-object = { version = "0.114.0" } target-lexicon = "0.12.0" gimli = { version = "0.31", default-features = false, features = ["write"] } -object = { version = "0.36", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } +object = { version = "0.36", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe", "unaligned"] } indexmap = "2.0.0" libloading = { version = "0.8.0", optional = true } smallvec = "1.8.1" +serde = { version = "1.0.203", features = ["derive"], optional = true } +postcard = { version = "1.0.8", default-features = false, features = ["use-std"], optional = true } [patch.crates-io] # Uncomment to use local checkout of cranelift @@ -35,8 +37,9 @@ smallvec = "1.8.1" [features] # Enable features not ready to be enabled when compiling as part of rustc -unstable-features = ["jit", "inline_asm_sym"] +unstable-features = ["jit", "inline_asm_sym", "lto"] jit = ["cranelift-jit", "libloading"] +lto = ["serde", "postcard", "cranelift-codegen/enable-serde", "cranelift-module/enable-serde"] inline_asm_sym = [] [package.metadata.rust-analyzer] diff --git a/build_system/build_sysroot.rs b/build_system/build_sysroot.rs index e47e98299..64c4e648f 100644 --- a/build_system/build_sysroot.rs +++ b/build_system/build_sysroot.rs @@ -243,6 +243,9 @@ fn build_clif_sysroot_for_triple( prefix.to_str().unwrap() )); } + rustflags.push("-Clto=thin".to_owned()); + rustflags.push("-Zdylib-lto".to_owned()); + rustflags.push("-Cembed-bitcode=yes".to_owned()); compiler.rustflags.extend(rustflags); let mut build_cmd = STANDARD_LIBRARY.build(&compiler, dirs); build_cmd.arg("--release"); @@ -253,6 +256,7 @@ fn build_clif_sysroot_for_triple( if compiler.triple.contains("apple") { build_cmd.env("CARGO_PROFILE_RELEASE_SPLIT_DEBUGINFO", "packed"); } + build_cmd.env("CARGO_PROFILE_RELEASE_LTO", "thin"); spawn_and_wait(build_cmd); for entry in fs::read_dir(build_dir.join("deps")).unwrap() { diff --git a/build_system/tests.rs b/build_system/tests.rs index 6d7ba5918..ff46702e8 100644 --- a/build_system/tests.rs +++ b/build_system/tests.rs @@ -57,7 +57,7 @@ impl TestCase { } const NO_SYSROOT_SUITE: &[TestCase] = &[ - TestCase::build_lib("build.mini_core", "example/mini_core.rs", "lib,dylib"), + TestCase::build_lib("build.mini_core", "example/mini_core.rs", "lib"), TestCase::build_lib("build.example", "example/example.rs", "lib"), TestCase::jit_bin("jit.mini_core_hello_world", "example/mini_core_hello_world.rs", "abc bcd"), TestCase::build_bin_and_run( @@ -400,6 +400,7 @@ impl<'a> TestRunner<'a> { } spawn_and_wait(jit_cmd); + /* eprintln!("[JIT-lazy] {testname}"); let mut jit_cmd = self.rustc_command([ "-Zunstable-options", @@ -413,6 +414,7 @@ impl<'a> TestRunner<'a> { jit_cmd.env("CG_CLIF_JIT_ARGS", args); } spawn_and_wait(jit_cmd); + */ } } } @@ -431,6 +433,7 @@ impl<'a> TestRunner<'a> { cmd.arg("--out-dir"); cmd.arg(BUILD_EXAMPLE_OUT_DIR.to_path(&self.dirs)); cmd.arg("-Cdebuginfo=2"); + cmd.arg("-Clto=thin"); cmd.arg("--target"); cmd.arg(&self.target_compiler.triple); cmd.arg("-Cpanic=abort"); diff --git a/example/mini_core.rs b/example/mini_core.rs index 3da215fe6..a39a57e08 100644 --- a/example/mini_core.rs +++ b/example/mini_core.rs @@ -786,6 +786,7 @@ struct PanicLocation { column: u32, } +/* #[no_mangle] #[cfg(not(all(windows, target_env = "gnu")))] pub fn get_tls() -> u8 { @@ -794,3 +795,4 @@ pub fn get_tls() -> u8 { A } +*/ diff --git a/example/mini_core_hello_world.rs b/example/mini_core_hello_world.rs index e47431e0f..f22a00aeb 100644 --- a/example/mini_core_hello_world.rs +++ b/example/mini_core_hello_world.rs @@ -332,11 +332,13 @@ fn main() { #[cfg(all(not(jit), not(all(windows, target_env = "gnu"))))] test_tls(); + /* #[cfg(all(not(jit), target_arch = "x86_64", any(target_os = "linux", target_os = "macos")))] unsafe { global_asm_test(); naked_test(); } + */ // Both statics have a reference that points to the same anonymous allocation. static REF1: &u8 = &42; @@ -361,6 +363,7 @@ fn stack_val_align() { assert_eq!(&a as *const Foo as usize % 8192, 0); } +/* #[cfg(all(not(jit), target_arch = "x86_64", any(target_os = "linux", target_os = "macos")))] extern "C" { fn global_asm_test(); @@ -385,6 +388,7 @@ global_asm! { ret " } +*/ #[cfg(all(not(jit), target_arch = "x86_64"))] #[naked] diff --git a/patches/0029-stdlib-dylib-crate-type.patch b/patches/0029-stdlib-dylib-crate-type.patch new file mode 100644 index 000000000..2f5c0a725 --- /dev/null +++ b/patches/0029-stdlib-dylib-crate-type.patch @@ -0,0 +1,25 @@ +From 0910cbe862990b0c3a17c67bca199ebb4452b0ec Mon Sep 17 00:00:00 2001 +From: bjorn3 <17426603+bjorn3@users.noreply.github.com> +Date: Tue, 28 Mar 2023 17:09:01 +0000 +Subject: [PATCH] Disable dylib crate type + +--- + library/std/Cargo.toml | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml +index 598a4bf..3e68680 100644 +--- a/library/std/Cargo.toml ++++ b/library/std/Cargo.toml +@@ -7,7 +7,7 @@ description = "The Rust Standard Library" + edition = "2021" + + [lib] +-crate-type = ["dylib", "rlib"] ++crate-type = ["rlib"] + + [dependencies] + alloc = { path = "../alloc", public = true } +-- +2.34.1 + diff --git a/src/base.rs b/src/base.rs index 057ec842d..d2aa52eac 100644 --- a/src/base.rs +++ b/src/base.rs @@ -252,6 +252,7 @@ pub(crate) fn compile_fn( } } + /* // Define debuginfo for function let debug_context = &mut cx.debug_context; cx.profiler.generic_activity("generate debug info").run(|| { @@ -263,6 +264,7 @@ pub(crate) fn compile_fn( ); } }); + */ } fn verify_func(tcx: TyCtxt<'_>, writer: &crate::pretty_clif::CommentWriter, func: &Function) { diff --git a/src/driver/aot.rs b/src/driver/aot.rs index 4880440bb..2bb0846e3 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -141,7 +141,7 @@ impl OngoingCodegen { } // Adapted from https://github.com/rust-lang/rust/blob/73476d49904751f8d90ce904e16dfbc278083d2c/compiler/rustc_codegen_ssa/src/back/write.rs#L547C1-L706C2 -fn produce_final_output_artifacts( +pub(super) fn produce_final_output_artifacts( sess: &Session, codegen_results: &CodegenResults, crate_output: &OutputFilenames, @@ -328,7 +328,7 @@ fn produce_final_output_artifacts( // These are used in linking steps and will be cleaned up afterward. } -fn make_module(sess: &Session, name: String) -> UnwindModule { +pub(super) fn make_module(sess: &Session, name: String) -> UnwindModule { let isa = crate::build_isa(sess); let mut builder = @@ -379,7 +379,7 @@ fn emit_cgu( }) } -fn emit_module( +pub(super) fn emit_module( output_filenames: &OutputFilenames, prof: &SelfProfilerRef, mut object: cranelift_object::object::write::Object<'_>, @@ -488,7 +488,7 @@ fn reuse_workproduct_for_cgu( }) } -fn codegen_cgu_content( +pub(super) fn codegen_cgu_content( tcx: TyCtxt<'_>, module: &mut dyn Module, cgu_name: rustc_span::Symbol, @@ -680,7 +680,7 @@ pub(crate) fn run_aot( }) } -fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option { +pub(super) fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option { let mut allocator_module = make_module(tcx.sess, "allocator_shim".to_string()); let created_alloc_shim = crate::allocator::codegen(tcx, &mut allocator_module); @@ -703,7 +703,7 @@ fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option { } } -fn emit_metadata_module(tcx: TyCtxt<'_>, metadata: &EncodedMetadata) -> CompiledModule { +pub(super) fn emit_metadata_module(tcx: TyCtxt<'_>, metadata: &EncodedMetadata) -> CompiledModule { tcx.sess.time("write compressed metadata", || { use rustc_middle::mir::mono::CodegenUnitNameBuilder; diff --git a/src/driver/jit.rs b/src/driver/jit.rs index 3405ce19f..1c3ebfe98 100644 --- a/src/driver/jit.rs +++ b/src/driver/jit.rs @@ -66,10 +66,15 @@ fn create_jit_module(tcx: TyCtxt<'_>, hotswap: bool) -> (UnwindModule let mut jit_builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names()); jit_builder.hotswap(hotswap); crate::compiler_builtins::register_functions_for_jit(&mut jit_builder); - jit_builder.symbol_lookup_fn(dep_symbol_lookup_fn(tcx.sess, crate_info)); + //jit_builder.symbol_lookup_fn(dep_symbol_lookup_fn(tcx.sess, crate_info)); jit_builder.symbol("__clif_jit_fn", clif_jit_fn as *const u8); let mut jit_module = UnwindModule::new(JITModule::new(jit_builder), false); + #[cfg(feature = "lto")] + for (_name, module) in super::lto::load_lto_modules(tcx, &crate_info) { + module.apply_to(&mut jit_module); + } + let cx = crate::CodegenCx::new(tcx, jit_module.isa(), false, sym::dummy_cgu_name); crate::allocator::codegen(tcx, &mut jit_module); diff --git a/src/driver/lto.rs b/src/driver/lto.rs new file mode 100644 index 000000000..8b99e4716 --- /dev/null +++ b/src/driver/lto.rs @@ -0,0 +1,250 @@ +//! FIXME + +// FIXME handle debuginfo + +// FIXME dedup as much with aot.rs as possible + +use object::{Object, ObjectSection}; +use rustc_codegen_ssa::{CodegenResults, CompiledModule, CrateInfo, ModuleKind}; +use rustc_metadata::EncodedMetadata; +use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; +use rustc_session::Session; +use rustc_session::config::OutputFilenames; + +use crate::driver::aot; +use crate::prelude::*; +use crate::serializable_module::SerializableModule; + +pub(crate) struct OngoingCodegen { + modules: Vec, + allocator_module: Option, + metadata_module: Option, + metadata: EncodedMetadata, + crate_info: CrateInfo, +} + +impl OngoingCodegen { + pub(crate) fn join( + self, + sess: &Session, + outputs: &OutputFilenames, + ) -> (CodegenResults, FxIndexMap) { + let mut modules = vec![]; + + for module in self.modules { + modules.push(module); + } + + sess.dcx().abort_if_errors(); + + let codegen_results = CodegenResults { + modules, + allocator_module: self.allocator_module, + metadata_module: self.metadata_module, + metadata: self.metadata, + crate_info: self.crate_info, + }; + + aot::produce_final_output_artifacts(sess, &codegen_results, outputs); + + (codegen_results, FxIndexMap::default()) + } +} + +fn make_module(sess: &Session) -> SerializableModule { + let isa = crate::build_isa(sess); + + SerializableModule::new(isa) +} + +fn compile_module_to_object( + tcx: TyCtxt<'_>, + serialize_module: SerializableModule, + name: String, +) -> Result { + let mut module = aot::make_module(tcx.sess, name.clone()); + serialize_module.apply_to(&mut module); + let product = module.finish(); + + aot::emit_module( + tcx.output_filenames(()), + &tcx.sess.prof, + product.object, + ModuleKind::Regular, + name, + &crate::debuginfo::producer(tcx.sess), + ) +} + +fn serialize_module_as_bitcode( + tcx: TyCtxt<'_>, + serialize_module: SerializableModule, + name: String, +) -> Result { + let mut object = object::write::Object::new( + object::BinaryFormat::Elf, + object::Architecture::Aarch64, + object::Endianness::Little, + ); + object.set_subsections_via_symbols(); + let symbol = object.add_symbol(object::write::Symbol { + name: b"cgclif_lto".to_vec(), + value: 0, + size: 0, + kind: object::write::SymbolKind::Data, + scope: object::write::SymbolScope::Compilation, + weak: false, + section: object::write::SymbolSection::Undefined, + flags: object::write::SymbolFlags::None, + }); + let section = + object.add_subsection(object::write::StandardSection::ReadOnlyData, b"cgclif_lto"); + object.add_symbol_data(symbol, section, &serialize_module.serialize(), 1); + + aot::emit_module( + tcx.output_filenames(()), + &tcx.sess.prof, + object, + ModuleKind::Regular, + name, + &crate::debuginfo::producer(tcx.sess), + ) +} + +fn module_codegen(tcx: TyCtxt<'_>, cgu_name: rustc_span::Symbol) -> CompiledModule { + let mut module = make_module(tcx.sess); + + let (mut cx, codegened_functions) = aot::codegen_cgu_content(tcx, &mut module, cgu_name); + + let cgu_name = cgu_name.as_str().to_owned(); + + match (move || { + cx.profiler.clone().verbose_generic_activity_with_arg("compile functions", &*cgu_name).run( + || { + let mut cached_context = Context::new(); + for codegened_func in codegened_functions { + crate::base::compile_fn( + &mut cx, + &mut cached_context, + &mut module, + codegened_func, + ); + } + }, + ); + + // FIXME handle inline asm + if !cx.global_asm.is_empty() { + tcx.sess.dcx().fatal("Inline asm is not yet supported in LTO mode"); + } + + let codegen_result = cx + .profiler + .verbose_generic_activity_with_arg("write object file", &*cgu_name) + .run(|| { + if tcx.crate_types() != [rustc_session::config::CrateType::Rlib] { + compile_module_to_object(tcx, module, cgu_name.clone()) + } else { + serialize_module_as_bitcode(tcx, module, cgu_name.clone()) + } + }); + codegen_result + })() { + Ok(res) => res, + Err(err) => tcx.sess.dcx().fatal(err), + } +} + +pub(super) fn load_lto_modules( + tcx: TyCtxt<'_>, + crate_info: &CrateInfo, +) -> Vec<(String, SerializableModule)> { + if tcx.crate_types() == [rustc_session::config::CrateType::Rlib] { + return vec![]; + } + + let mut each_linked_rlib_for_lto = Vec::new(); + drop(rustc_codegen_ssa::back::link::each_linked_rlib(&crate_info, None, &mut |cnum, path| { + if rustc_codegen_ssa::back::link::ignored_for_lto(tcx.sess, &crate_info, cnum) { + return; + } + each_linked_rlib_for_lto.push((cnum, path.to_path_buf())); + })); + + let mut modules = vec![]; + + for (_cnum, path) in each_linked_rlib_for_lto { + let archive_data = unsafe { + rustc_data_structures::memmap::Mmap::map( + std::fs::File::open(&path).expect("couldn't open rlib"), + ) + .expect("couldn't map rlib") + }; + let archive = + object::read::archive::ArchiveFile::parse(&*archive_data).expect("wanted an rlib"); + let obj_files = archive + .members() + .filter_map(|child| { + child + .ok() + .and_then(|c| std::str::from_utf8(c.name()).ok().map(|name| (name.trim(), c))) + }) + .filter(|&(name, _)| rustc_codegen_ssa::looks_like_rust_object_file(name)); + for (name, child) in obj_files { + let lto_object = + object::read::File::parse(child.data(&*archive_data).expect("corrupt rlib")) + .unwrap(); + let module = SerializableModule::deserialize( + lto_object.section_by_name(".rodata.cgclif_lto").unwrap().data().unwrap(), + crate::build_isa(tcx.sess), + ); + modules.push((name.to_owned(), module)); + } + } + + modules +} + +pub(crate) fn run_aot( + tcx: TyCtxt<'_>, + metadata: EncodedMetadata, + need_metadata_module: bool, +) -> Box { + // FIXME handle `-Ctarget-cpu=native` + let target_cpu = match tcx.sess.opts.cg.target_cpu { + Some(ref name) => name, + None => tcx.sess.target.cpu.as_ref(), + } + .to_owned(); + + let crate_info = CrateInfo::new(tcx, target_cpu); + + let cgus = if tcx.sess.opts.output_types.should_codegen() { + tcx.collect_and_partition_mono_items(()).1 + } else { + // If only `--emit metadata` is used, we shouldn't perform any codegen. + // Also `tcx.collect_and_partition_mono_items` may panic in that case. + return Box::new(OngoingCodegen { + modules: vec![], + allocator_module: None, + metadata_module: None, + metadata, + crate_info, + }); + }; + + let mut modules = tcx.sess.time("codegen mono items", || { + cgus.iter().map(|cgu| module_codegen(tcx, cgu.name())).collect::>() + }); + + for (name, module) in load_lto_modules(tcx, &crate_info) { + modules.push(compile_module_to_object(tcx, module, name).unwrap()); + } + + let allocator_module = aot::emit_allocator_module(tcx); + + let metadata_module = + if need_metadata_module { Some(aot::emit_metadata_module(tcx, &metadata)) } else { None }; + + Box::new(OngoingCodegen { modules, allocator_module, metadata_module, metadata, crate_info }) +} diff --git a/src/driver/mod.rs b/src/driver/mod.rs index fb0eed07c..f04278a70 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs @@ -13,6 +13,8 @@ use crate::prelude::*; pub(crate) mod aot; #[cfg(feature = "jit")] pub(crate) mod jit; +#[cfg(feature = "lto")] +pub(crate) mod lto; fn predefine_mono_items<'tcx>( tcx: TyCtxt<'tcx>, diff --git a/src/lib.rs b/src/lib.rs index b55ae3945..92942f1ae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,6 +75,8 @@ mod num; mod optimize; mod pointer; mod pretty_clif; +#[cfg(feature = "lto")] +mod serializable_module; mod toolchain; mod trap; mod unsize; @@ -166,10 +168,12 @@ impl CodegenBackend for CraneliftCodegenBackend { fn init(&self, sess: &Session) { use rustc_session::config::{InstrumentCoverage, Lto}; match sess.lto() { - Lto::No | Lto::ThinLocal => {} - Lto::Thin | Lto::Fat => { - sess.dcx().warn("LTO is not supported. You may get a linker error.") + Lto::No | Lto::ThinLocal => { + if sess.opts.crate_name.as_deref() != Some("___") { + sess.dcx().fatal("No LTO"); + } } + Lto::Thin | Lto::Fat => {} } if sess.opts.cg.instrument_coverage() != InstrumentCoverage::No { @@ -207,13 +211,35 @@ impl CodegenBackend for CraneliftCodegenBackend { metadata: EncodedMetadata, need_metadata_module: bool, ) -> Box { + use rustc_session::config::Lto; + tcx.dcx().abort_if_errors(); let config = self.config.clone().unwrap_or_else(|| { BackendConfig::from_opts(&tcx.sess.opts.cg.llvm_args) .unwrap_or_else(|err| tcx.sess.dcx().fatal(err)) }); match config.codegen_mode { - CodegenMode::Aot => driver::aot::run_aot(tcx, metadata, need_metadata_module), + CodegenMode::Aot => { + match tcx.sess.lto() { + Lto::No | Lto::ThinLocal => { + driver::aot::run_aot(tcx, metadata, need_metadata_module) + } + Lto::Thin | Lto::Fat => { + if tcx.crate_name(LOCAL_CRATE).as_str() == "compiler_builtins" { + // FIXME remove special case once inline asm is supported in LTO mode + driver::aot::run_aot(tcx, metadata, need_metadata_module) + } else { + #[cfg(feature = "lto")] + return driver::lto::run_aot(tcx, metadata, need_metadata_module); + + #[cfg(not(feature = "lto"))] + tcx.dcx().fatal( + "LTO support was disabled when compiling rustc_codegen_cranelift", + ); + } + } + } + } CodegenMode::Jit | CodegenMode::JitLazy => { #[cfg(feature = "jit")] driver::jit::run_jit(tcx, config.codegen_mode, config.jit_args); @@ -230,9 +256,19 @@ impl CodegenBackend for CraneliftCodegenBackend { sess: &Session, outputs: &OutputFilenames, ) -> (CodegenResults, FxIndexMap) { - let _timer = sess.timer("finish_ongoing_codegen"); - - ongoing_codegen.downcast::().unwrap().join(sess, outputs) + match ongoing_codegen.downcast::() { + Ok(ongoing_codegen) => ongoing_codegen.join(sess, outputs), + Err(ongoing_codegen) => { + #[cfg(feature = "lto")] + return ongoing_codegen + .downcast::() + .unwrap() + .join(sess, outputs); + + #[cfg(not(feature = "lto"))] + unreachable!(); + } + } } } diff --git a/src/serializable_module.rs b/src/serializable_module.rs new file mode 100644 index 000000000..4a458532d --- /dev/null +++ b/src/serializable_module.rs @@ -0,0 +1,249 @@ +use std::collections::BTreeMap; +use std::sync::Arc; + +use cranelift_codegen::control::ControlPlane; +use cranelift_codegen::entity::SecondaryMap; +use cranelift_codegen::ir::{Signature, UserExternalName}; +use cranelift_codegen::isa::TargetIsa; +use cranelift_codegen::FinalizedMachReloc; +use cranelift_module::{DataId, ModuleDeclarations, ModuleError, ModuleRelocTarget, ModuleResult}; + +use crate::prelude::*; + +pub(super) struct SerializableModule { + isa: Arc, + inner: SerializableModuleInner, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +struct SerializableModuleInner { + declarations: ModuleDeclarations, + functions: BTreeMap, + data_objects: BTreeMap, +} + +impl SerializableModule { + pub(crate) fn new(isa: Arc) -> Self { + SerializableModule { + isa, + inner: SerializableModuleInner { + declarations: ModuleDeclarations::default(), + functions: BTreeMap::new(), + data_objects: BTreeMap::new(), + }, + } + } + + pub(crate) fn serialize(self) -> Vec { + postcard::to_stdvec(&self.inner).unwrap() + } + + pub(crate) fn deserialize(blob: &[u8], isa: Arc) -> SerializableModule { + // FIXME check isa compatibility + SerializableModule { isa, inner: postcard::from_bytes(blob).unwrap() } + } + + pub(crate) fn apply_to(self, module: &mut dyn Module) { + let mut function_map: SecondaryMap> = SecondaryMap::new(); + let mut data_object_map: SecondaryMap> = SecondaryMap::new(); + + let mut remap_func_id = + |module: &mut dyn Module, declarations: &ModuleDeclarations, func_id: FuncId| { + if function_map[func_id].is_none() { + let decl = declarations.get_function_decl(func_id); + function_map[func_id] = Some(if let Some(name) = &decl.name { + module.declare_function(name, decl.linkage, &decl.signature).unwrap() + } else { + module.declare_anonymous_function(&decl.signature).unwrap() + }); + } + function_map[func_id].unwrap() + }; + + let mut remap_data_id = + |module: &mut dyn Module, declarations: &ModuleDeclarations, data_id: DataId| { + if data_object_map[data_id].is_none() { + let decl = declarations.get_data_decl(data_id); + data_object_map[data_id] = Some(if let Some(name) = &decl.name { + module.declare_data(name, decl.linkage, decl.writable, decl.tls).unwrap() + } else { + module.declare_anonymous_data(decl.writable, decl.tls).unwrap() + }); + } + data_object_map[data_id].unwrap() + }; + + for (func_id, mut func) in self.inner.functions { + let func_id = remap_func_id(module, &self.inner.declarations, func_id); + let user_named_funcs = func.params.user_named_funcs().clone(); + for (ext_name_ref, ext_name) in user_named_funcs { + if ext_name.namespace == 0 { + func.params.reset_user_func_name( + ext_name_ref, + UserExternalName::new( + 0, + remap_func_id( + module, + &self.inner.declarations, + FuncId::from_u32(ext_name.index), + ) + .as_u32(), + ), + ); + } else if ext_name.namespace == 1 { + func.params.reset_user_func_name( + ext_name_ref, + UserExternalName::new( + 1, + remap_data_id( + module, + &self.inner.declarations, + DataId::from_u32(ext_name.index), + ) + .as_u32(), + ), + ); + } else { + unreachable!(); + } + } + module.define_function(func_id, &mut Context::for_function(func)).unwrap(); + } + + for (data_id, mut data) in self.inner.data_objects { + let data_id = remap_data_id(module, &self.inner.declarations, data_id); + for ext_name in data + .function_decls + .iter_mut() + .map(|(_, ext_name)| ext_name) + .chain(data.data_decls.iter_mut().map(|(_, ext_name)| ext_name)) + { + match *ext_name { + ModuleRelocTarget::User { namespace, ref mut index } => { + if namespace == 0 { + *index = remap_func_id( + module, + &self.inner.declarations, + FuncId::from_u32(*index), + ) + .as_u32(); + } else if namespace == 1 { + *index = remap_data_id( + module, + &self.inner.declarations, + DataId::from_u32(*index), + ) + .as_u32(); + } else { + unreachable!(); + } + } + ModuleRelocTarget::KnownSymbol(_) + | ModuleRelocTarget::LibCall(_) + | ModuleRelocTarget::FunctionOffset(_, _) => {} + } + } + module.define_data(data_id, &data).unwrap(); + } + + //todo!(); + } +} + +impl Module for SerializableModule { + fn isa(&self) -> &dyn TargetIsa { + &*self.isa + } + + fn declarations(&self) -> &ModuleDeclarations { + &self.inner.declarations + } + + fn declare_function( + &mut self, + name: &str, + linkage: Linkage, + signature: &Signature, + ) -> ModuleResult { + let (id, _linkage) = self.inner.declarations.declare_function(name, linkage, signature)?; + + Ok(id) + } + + fn declare_anonymous_function(&mut self, signature: &Signature) -> ModuleResult { + Ok(self.inner.declarations.declare_anonymous_function(signature)?) + } + + fn declare_data( + &mut self, + name: &str, + linkage: Linkage, + writable: bool, + tls: bool, + ) -> ModuleResult { + let (id, _linkage) = self.inner.declarations.declare_data(name, linkage, writable, tls)?; + + Ok(id) + } + + fn declare_anonymous_data(&mut self, writable: bool, tls: bool) -> ModuleResult { + Ok(self.inner.declarations.declare_anonymous_data(writable, tls)?) + } + + fn define_function_with_control_plane( + &mut self, + func_id: FuncId, + ctx: &mut Context, + ctrl_plane: &mut ControlPlane, + ) -> ModuleResult<()> { + let decl = self.inner.declarations.get_function_decl(func_id); + if !decl.linkage.is_definable() { + return Err(ModuleError::InvalidImportDefinition( + decl.name.as_deref().unwrap_or("").to_owned(), + )); + } + + if self.inner.functions.get(&func_id).is_some() { + return Err(ModuleError::DuplicateDefinition( + decl.name.as_deref().unwrap_or("").to_owned(), + )); + } + + ctx.verify_if(&*self.isa)?; + ctx.optimize(&*self.isa, ctrl_plane)?; + + self.inner.functions.insert(func_id, ctx.func.clone()); + + Ok(()) + } + + fn define_function_bytes( + &mut self, + _func_id: FuncId, + _func: &Function, + _alignment: u64, + _bytes: &[u8], + _relocs: &[FinalizedMachReloc], + ) -> ModuleResult<()> { + unimplemented!() + } + + fn define_data(&mut self, data_id: DataId, data: &DataDescription) -> ModuleResult<()> { + let decl = self.inner.declarations.get_data_decl(data_id); + if !decl.linkage.is_definable() { + return Err(ModuleError::InvalidImportDefinition( + decl.name.as_deref().unwrap_or("").to_owned(), + )); + } + + if self.inner.data_objects.get(&data_id).is_some() { + return Err(ModuleError::DuplicateDefinition( + decl.name.as_deref().unwrap_or("").to_owned(), + )); + } + + self.inner.data_objects.insert(data_id, data.clone()); + + Ok(()) + } +}