From 741a7a7b8ee3c77ab500d113009bed3b18c30422 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Thu, 14 Dec 2023 17:52:59 -0500 Subject: [PATCH] (crudely) implement MIR-only rlibs --- .../src/back/symbol_export.rs | 10 +++- compiler/rustc_interface/src/tests.rs | 1 + compiler/rustc_metadata/src/rmeta/encoder.rs | 9 +-- compiler/rustc_middle/src/mir/mono.rs | 15 +++++ compiler/rustc_middle/src/ty/context.rs | 8 +++ compiler/rustc_monomorphize/src/collector.rs | 56 ++++++++++++++++--- .../rustc_monomorphize/src/partitioning.rs | 15 ++++- compiler/rustc_session/src/options.rs | 2 + 8 files changed, 101 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 94841ab7b33e0..6b9843c29b84c 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -227,8 +227,14 @@ fn exported_symbols_provider_local( if allocator_kind_for_codegen(tcx).is_some() { for symbol_name in ALLOCATOR_METHODS .iter() - .map(|method| format!("__rust_{}", method.name)) - .chain(["__rust_alloc_error_handler".to_string(), OomStrategy::SYMBOL.to_string()]) + .flat_map(|method| { + [format!("__rust_{}", method.name), format!("__rdl_{}", method.name)] + }) + .chain([ + "__rust_alloc_error_handler".to_string(), + OomStrategy::SYMBOL.to_string(), + "__rg_oom".to_string(), + ]) { let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &symbol_name)); diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index f3f59b05682db..47cf7cdf0588f 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -776,6 +776,7 @@ fn test_unstable_options_tracking_hash() { tracked!(mir_emit_retag, true); tracked!(mir_enable_passes, vec![("DestProp".to_string(), false)]); tracked!(mir_keep_place_mention, true); + tracked!(mir_only_libs, true); tracked!(mir_opt_level, Some(4)); tracked!(move_size_limit, Some(4096)); tracked!(mutable_noalias, false); diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index a458b528a97cb..18814672b9ec6 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1037,12 +1037,13 @@ fn should_encode_mir( reachable_set: &LocalDefIdSet, def_id: LocalDefId, ) -> (bool, bool) { + let opts = &tcx.sess.opts; + let mir_required = opts.unstable_opts.always_encode_mir || opts.unstable_opts.mir_only_libs; match tcx.def_kind(def_id) { // Constructors DefKind::Ctor(_, _) => { - let mir_opt_base = tcx.sess.opts.output_types.should_codegen() - || tcx.sess.opts.unstable_opts.always_encode_mir; - (true, mir_opt_base) + let opt = mir_required || opts.output_types.should_codegen(); + (true, opt) } // Constants DefKind::AnonConst @@ -1055,7 +1056,7 @@ fn should_encode_mir( // Full-fledged functions + closures DefKind::AssocFn | DefKind::Fn | DefKind::Closure => { let generics = tcx.generics_of(def_id); - let opt = tcx.sess.opts.unstable_opts.always_encode_mir + let opt = mir_required || (tcx.sess.opts.output_types.should_codegen() && reachable_set.contains(&def_id) && (generics.requires_monomorphization(tcx) diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 91fdf0b312991..1a4777eb71646 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -98,6 +98,10 @@ impl<'tcx> MonoItem<'tcx> { } pub fn instantiation_mode(&self, tcx: TyCtxt<'tcx>) -> InstantiationMode { + // Always do LocalCopy codegen when building a MIR-only rlib + if tcx.building_mir_only_lib() { + return InstantiationMode::LocalCopy; + } let generate_cgu_internal_copies = tcx .sess .opts @@ -108,6 +112,17 @@ impl<'tcx> MonoItem<'tcx> { match *self { MonoItem::Fn(ref instance) => { + // See the check in MirUsedCollector for building_mir_only_bin for background. + // Here, we need to give the symbols that the collector feeds to use GloballyShared + // linkage so that CGU partitioning doesn't try to assign them lazily, then find no + // uses of them. They might only be used by an upstream crate, so we need to force + // codegen, which is what GloballyShared does. + if tcx.building_mir_only_bin() + && tcx.codegen_fn_attrs(instance.def_id()).contains_extern_indicator() + { + return InstantiationMode::GloballyShared { may_conflict: false }; + } + let entry_def_id = tcx.entry_fn(()).map(|(id, _)| id); // If this function isn't inlined or otherwise has an extern // indicator, then we'll be creating a globally shared version. diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 6807eacb7f177..87d151e8c76b7 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1028,6 +1028,14 @@ impl<'tcx> TyCtxt<'tcx> { pub fn dcx(self) -> &'tcx DiagCtxt { self.sess.dcx() } + + pub fn building_mir_only_lib(self) -> bool { + self.sess.opts.unstable_opts.mir_only_libs && self.crate_types() == &[CrateType::Rlib] + } + + pub fn building_mir_only_bin(self) -> bool { + self.sess.opts.unstable_opts.mir_only_libs && self.crate_types() == &[CrateType::Executable] + } } impl<'tcx> TyCtxtAt<'tcx> { diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 44beafa08736e..006102d141156 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -170,6 +170,7 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_hir::lang_items::LangItem; +use rustc_middle::middle::exported_symbols::ExportedSymbol; use rustc_middle::mir::interpret::{AllocId, ErrorHandled, GlobalAlloc, Scalar}; use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; use rustc_middle::mir::visit::Visitor as MirVisitor; @@ -975,8 +976,17 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) -> }; if tcx.is_foreign_item(def_id) { - // Foreign items are always linked against, there's no way of instantiating them. - return false; + if tcx.sess.opts.unstable_opts.mir_only_libs { + return tcx.is_mir_available(instance.def_id()); + } else { + // Foreign items are always linked against, there's no way of instantiating them. + return false; + } + } + + if tcx.sess.opts.unstable_opts.mir_only_libs { + let has_mir = tcx.is_mir_available(instance.def_id()); + return has_mir || matches!(tcx.def_kind(instance.def_id()), DefKind::Static(_)); } if def_id.is_local() { @@ -984,6 +994,11 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) -> return true; } + if let DefKind::Static(_) = tcx.def_kind(def_id) { + // We cannot monomorphize statics from upstream crates. + return false; + } + if tcx.is_reachable_non_generic(def_id) || instance.polymorphize(tcx).upstream_monomorphization(tcx).is_some() { @@ -991,11 +1006,6 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) -> return false; } - if let DefKind::Static(_) = tcx.def_kind(def_id) { - // We cannot monomorphize statics from upstream crates. - return false; - } - if !tcx.is_mir_available(def_id) { tcx.dcx().emit_fatal(NoOptimizedMir { span: tcx.def_span(def_id), @@ -1295,6 +1305,38 @@ impl<'v> RootCollector<'_, 'v> { .unwrap(); self.output.push(create_fn_mono_item(self.tcx, start_instance, DUMMY_SP)); + + // An upstream extern function may be used anywhere in the dependency tree, so we + // cannot do any reachability analysis on them. We blindly monomorphize every + // extern function declared anywhere in our dependency tree. We must give them + // GloballyShared codegen because we don't know if the only call to an upstream + // extern function is also upstream: We don't have reachability information. All we + // can do is codegen all extern functions and pray for the linker to delete the + // ones that are reachable. + if self.tcx.building_mir_only_bin() { + for (symbol, _info) in + self.tcx.crates(()).into_iter().flat_map(|krate| self.tcx.exported_symbols(*krate)) + { + let def_id = match symbol { + ExportedSymbol::NonGeneric(def_id) => def_id, + _ => { + eprintln!("Not handling exported symbol {:?}", symbol); + continue; + } + }; + if self.tcx.def_kind(def_id) != DefKind::Fn { + eprintln!("Not handling exported def {:?}", def_id); + continue; + } + let instance = Instance::mono(self.tcx, *def_id); + // FIXME: This is probably not the right span. What is? + let item = create_fn_mono_item(self.tcx, instance, DUMMY_SP); + // FIXME: Do we need this check? + if !self.output.iter().any(|out| out.node.def_id() == *def_id) { + self.output.push(item); + } + } + } } } diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 08e628408394e..3ec9dd292e3ea 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -141,6 +141,10 @@ fn partition<'tcx, I>( where I: Iterator>, { + if tcx.sess.opts.unstable_opts.mir_only_libs { + assert!(tcx.building_mir_only_lib() || tcx.building_mir_only_bin()); + } + let _prof_timer = tcx.prof.generic_activity("cgu_partitioning"); let cx = &PartitioningCx { tcx, usage_map }; @@ -211,6 +215,9 @@ where let cgu_name_builder = &mut CodegenUnitNameBuilder::new(cx.tcx); let cgu_name_cache = &mut FxHashMap::default(); + let start_fn = cx.tcx.lang_items().start_fn(); + let entry_fn = cx.tcx.entry_fn(()).map(|(id, _)| id); + for mono_item in mono_items { // Handle only root (GloballyShared) items directly here. Inlined (LocalCopy) items // are handled at the bottom of the loop based on reachability, with one exception. @@ -219,7 +226,8 @@ where match mono_item.instantiation_mode(cx.tcx) { InstantiationMode::GloballyShared { .. } => {} InstantiationMode::LocalCopy => { - if Some(mono_item.def_id()) != cx.tcx.lang_items().start_fn() { + let def_id = mono_item.def_id(); + if ![start_fn, entry_fn].contains(&Some(def_id)) { continue; } } @@ -241,7 +249,7 @@ where let cgu = codegen_units.entry(cgu_name).or_insert_with(|| CodegenUnit::new(cgu_name)); - let mut can_be_internalized = true; + let mut can_be_internalized = false; let (linkage, visibility) = mono_item_linkage_and_visibility( cx.tcx, &mono_item, @@ -256,6 +264,9 @@ where cgu.items_mut() .insert(mono_item, MonoItemData { inlined: false, linkage, visibility, size_estimate }); + if cx.tcx.building_mir_only_lib() { + continue; + } // Get all inlined items that are reachable from `mono_item` without // going via another root item. This includes drop-glue, functions from // external crates, and local functions the definition of which is diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 842bf1d60f66d..3b06437da34ae 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1705,6 +1705,8 @@ options! { mir_keep_place_mention: bool = (false, parse_bool, [TRACKED], "keep place mention MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \ (default: no)"), + mir_only_libs: bool = (false, parse_bool, [TRACKED], + "only generate MIR when building libraries (default: no)"), #[rustc_lint_opt_deny_field_access("use `Session::mir_opt_level` instead of this field")] mir_opt_level: Option = (None, parse_opt_number, [TRACKED], "MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"),