From 0d508bb0cd681bf15f7861670cd38a1b352d2e40 Mon Sep 17 00:00:00 2001 From: Mohammad Omidvar Date: Mon, 15 Jul 2024 19:54:47 +0000 Subject: [PATCH 01/18] Introduce and provide a hook for `should_codegen_locally` --- compiler/rustc_middle/src/hooks/mod.rs | 4 ++++ compiler/rustc_monomorphize/src/collector.rs | 15 +++++++++++++-- compiler/rustc_monomorphize/src/lib.rs | 3 ++- compiler/rustc_monomorphize/src/partitioning.rs | 4 +++- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index bf10a71dbaec1..75dc685a16aee 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -103,6 +103,10 @@ declare_hooks! { /// Create a list-like THIR representation for debugging. hook thir_flat(key: LocalDefId) -> String; + + /// Returns `true` if we should codegen an instance in the local crate, or returns `false` if we + /// can just link to the upstream crate and therefore don't need a mono item. + hook should_codegen_locally(instance: crate::ty::Instance<'tcx>) -> bool; } #[cold] diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index bfd505c06726d..27d27885ea8d8 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -228,6 +228,7 @@ use rustc_middle::ty::{ self, AssocKind, GenericParamDefKind, Instance, InstanceKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, VtblEntry, }; +use rustc_middle::util::Providers; use rustc_middle::{bug, span_bug}; use rustc_session::config::EntryFnType; use rustc_session::Limit; @@ -930,7 +931,7 @@ fn visit_instance_use<'tcx>( /// Returns `true` if we should codegen an instance in the local crate, or returns `false` if we /// can just link to the upstream crate and therefore don't need a mono item. -pub(crate) fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> bool { +pub(crate) fn should_codegen_locally_hook<'tcx>(tcx: TyCtxtAt<'tcx>, instance: Instance<'tcx>) -> bool { let Some(def_id) = instance.def.def_id_if_not_guaranteed_local_codegen() else { return true; }; @@ -946,7 +947,7 @@ pub(crate) fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance } if tcx.is_reachable_non_generic(def_id) - || instance.polymorphize(tcx).upstream_monomorphization(tcx).is_some() + || instance.polymorphize(*tcx).upstream_monomorphization(*tcx).is_some() { // We can link to the item in question, no instance needed in this crate. return false; @@ -967,6 +968,12 @@ pub(crate) fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance true } +/// Returns `true` if we should codegen an instance in the local crate, or returns `false` if we +/// can just link to the upstream crate and therefore don't need a mono item. +pub(crate) fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> bool { + tcx.should_codegen_locally(instance) +} + /// For a given pair of source and target type that occur in an unsizing coercion, /// this function finds the pair of types that determines the vtable linking /// them. @@ -1613,3 +1620,7 @@ pub(crate) fn collect_crate_mono_items<'tcx>( (mono_items, state.usage_map.into_inner()) } + +pub fn provide(providers: &mut Providers) { + providers.hooks.should_codegen_locally = should_codegen_locally_hook; +} diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index aa3b4cd5b6782..d169f08606636 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -5,12 +5,13 @@ use rustc_hir::lang_items::LangItem; use rustc_middle::bug; -use rustc_middle::query::{Providers, TyCtxtAt}; +use rustc_middle::query::TyCtxtAt; use rustc_middle::traits; use rustc_middle::ty::adjustment::CustomCoerceUnsized; use rustc_middle::ty::Instance; use rustc_middle::ty::TyCtxt; use rustc_middle::ty::{self, Ty}; +use rustc_middle::util::Providers; use rustc_span::def_id::DefId; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::ErrorGuaranteed; diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 9a7c488833a1a..8c7c5e0074abf 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -112,9 +112,9 @@ use rustc_middle::mir::mono::{ CodegenUnit, CodegenUnitNameBuilder, InstantiationMode, Linkage, MonoItem, MonoItemData, Visibility, }; -use rustc_middle::query::Providers; use rustc_middle::ty::print::{characteristic_def_id_of_type, with_no_trimmed_paths}; use rustc_middle::ty::{self, visit::TypeVisitableExt, InstanceKind, TyCtxt}; +use rustc_middle::util::Providers; use rustc_session::config::{DumpMonoStatsFormat, SwitchWithOptPath}; use rustc_session::CodegenUnits; use rustc_span::symbol::Symbol; @@ -1314,4 +1314,6 @@ pub fn provide(providers: &mut Providers) { .find(|cgu| cgu.name() == name) .unwrap_or_else(|| panic!("failed to find cgu with name {name:?}")) }; + + collector::provide(providers); } From 14430e66be02266ca59bdddd38d88a27f2e02917 Mon Sep 17 00:00:00 2001 From: Mohammad Omidvar Date: Mon, 15 Jul 2024 19:58:44 +0000 Subject: [PATCH 02/18] Use the hook on tcx instead of the local function --- compiler/rustc_monomorphize/src/collector.rs | 40 +++++++++----------- compiler/rustc_monomorphize/src/lib.rs | 4 +- 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 27d27885ea8d8..3655a677ba0ad 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -400,7 +400,7 @@ fn collect_items_rec<'tcx>( let instance = Instance::mono(tcx, def_id); // Sanity check whether this ended up being collected accidentally - debug_assert!(should_codegen_locally(tcx, instance)); + debug_assert!(tcx.should_codegen_locally(instance)); let DefKind::Static { nested, .. } = tcx.def_kind(def_id) else { bug!() }; // Nested statics have no type. @@ -432,7 +432,7 @@ fn collect_items_rec<'tcx>( } MonoItem::Fn(instance) => { // Sanity check whether this ended up being collected accidentally - debug_assert!(should_codegen_locally(tcx, instance)); + debug_assert!(tcx.should_codegen_locally(instance)); // Keep track of the monomorphization recursion depth recursion_depth_reset = Some(check_recursion_limit( @@ -476,7 +476,7 @@ fn collect_items_rec<'tcx>( } hir::InlineAsmOperand::SymStatic { path: _, def_id } => { let instance = Instance::mono(tcx, *def_id); - if should_codegen_locally(tcx, instance) { + if tcx.should_codegen_locally(instance) { trace!("collecting static {:?}", def_id); used_items.push(dummy_spanned(MonoItem::Static(*def_id))); } @@ -713,7 +713,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { if let ty::Closure(def_id, args) = *source_ty.kind() { let instance = Instance::resolve_closure(self.tcx, def_id, args, ty::ClosureKind::FnOnce); - if should_codegen_locally(self.tcx, instance) { + if self.tcx.should_codegen_locally(instance) { self.used_items.push(create_fn_mono_item(self.tcx, instance, span)); } } else { @@ -723,7 +723,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { mir::Rvalue::ThreadLocalRef(def_id) => { assert!(self.tcx.is_thread_local_static(def_id)); let instance = Instance::mono(self.tcx, def_id); - if should_codegen_locally(self.tcx, instance) { + if self.tcx.should_codegen_locally(instance) { trace!("collecting thread-local static {:?}", def_id); self.used_items.push(respan(span, MonoItem::Static(def_id))); } @@ -750,7 +750,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { let tcx = self.tcx; let push_mono_lang_item = |this: &mut Self, lang_item: LangItem| { let instance = Instance::mono(tcx, tcx.require_lang_item(lang_item, Some(source))); - if should_codegen_locally(tcx, instance) { + if tcx.should_codegen_locally(instance) { this.used_items.push(create_fn_mono_item(tcx, instance, source)); } }; @@ -784,7 +784,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { } mir::InlineAsmOperand::SymStatic { def_id } => { let instance = Instance::mono(self.tcx, def_id); - if should_codegen_locally(self.tcx, instance) { + if self.tcx.should_codegen_locally(instance) { trace!("collecting asm sym static {:?}", def_id); self.used_items.push(respan(source, MonoItem::Static(def_id))); } @@ -874,7 +874,7 @@ fn visit_instance_use<'tcx>( output: &mut MonoItems<'tcx>, ) { debug!("visit_item_use({:?}, is_direct_call={:?})", instance, is_direct_call); - if !should_codegen_locally(tcx, instance) { + if !tcx.should_codegen_locally(instance) { return; } if let ty::InstanceKind::Intrinsic(def_id) = instance.def { @@ -886,13 +886,13 @@ fn visit_instance_use<'tcx>( // codegen a call to that function without generating code for the function itself. let def_id = tcx.require_lang_item(LangItem::PanicNounwind, None); let panic_instance = Instance::mono(tcx, def_id); - if should_codegen_locally(tcx, panic_instance) { + if tcx.should_codegen_locally(panic_instance) { output.push(create_fn_mono_item(tcx, panic_instance, source)); } } else if tcx.has_attr(def_id, sym::rustc_intrinsic) { // Codegen the fallback body of intrinsics with fallback bodies let instance = ty::Instance::new(def_id, instance.args); - if should_codegen_locally(tcx, instance) { + if tcx.should_codegen_locally(instance) { output.push(create_fn_mono_item(tcx, instance, source)); } } @@ -931,7 +931,7 @@ fn visit_instance_use<'tcx>( /// Returns `true` if we should codegen an instance in the local crate, or returns `false` if we /// can just link to the upstream crate and therefore don't need a mono item. -pub(crate) fn should_codegen_locally_hook<'tcx>(tcx: TyCtxtAt<'tcx>, instance: Instance<'tcx>) -> bool { +fn should_codegen_locally<'tcx>(tcx: TyCtxtAt<'tcx>, instance: Instance<'tcx>) -> bool { let Some(def_id) = instance.def.def_id_if_not_guaranteed_local_codegen() else { return true; }; @@ -968,12 +968,6 @@ pub(crate) fn should_codegen_locally_hook<'tcx>(tcx: TyCtxtAt<'tcx>, instance: I true } -/// Returns `true` if we should codegen an instance in the local crate, or returns `false` if we -/// can just link to the upstream crate and therefore don't need a mono item. -pub(crate) fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> bool { - tcx.should_codegen_locally(instance) -} - /// For a given pair of source and target type that occur in an unsizing coercion, /// this function finds the pair of types that determines the vtable linking /// them. @@ -1134,7 +1128,7 @@ fn create_mono_items_for_vtable_methods<'tcx>( None } VtblEntry::Method(instance) => { - Some(*instance).filter(|instance| should_codegen_locally(tcx, *instance)) + Some(*instance).filter(|instance| tcx.should_codegen_locally(*instance)) } }) .map(|item| create_fn_mono_item(tcx, item, source)); @@ -1151,7 +1145,7 @@ fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIt GlobalAlloc::Static(def_id) => { assert!(!tcx.is_thread_local_static(def_id)); let instance = Instance::mono(tcx, def_id); - if should_codegen_locally(tcx, instance) { + if tcx.should_codegen_locally(instance) { trace!("collecting static {:?}", def_id); output.push(dummy_spanned(MonoItem::Static(def_id))); } @@ -1169,7 +1163,7 @@ fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIt } } GlobalAlloc::Function { instance, .. } => { - if should_codegen_locally(tcx, instance) { + if tcx.should_codegen_locally(instance) { trace!("collecting {:?} with {:#?}", alloc_id, instance); output.push(create_fn_mono_item(tcx, instance, DUMMY_SP)); } @@ -1291,7 +1285,7 @@ fn visit_mentioned_item<'tcx>( if let ty::Closure(def_id, args) = *source_ty.kind() { let instance = Instance::resolve_closure(tcx, def_id, args, ty::ClosureKind::FnOnce); - if should_codegen_locally(tcx, instance) { + if tcx.should_codegen_locally(instance) { output.push(create_fn_mono_item(tcx, instance, span)); } } else { @@ -1564,7 +1558,7 @@ fn create_mono_items_for_default_impls<'tcx>( let instance = ty::Instance::expect_resolve(tcx, param_env, method.def_id, args, DUMMY_SP); let mono_item = create_fn_mono_item(tcx, instance, DUMMY_SP); - if mono_item.node.is_instantiable(tcx) && should_codegen_locally(tcx, instance) { + if mono_item.node.is_instantiable(tcx) && tcx.should_codegen_locally(instance) { output.push(mono_item); } } @@ -1622,5 +1616,5 @@ pub(crate) fn collect_crate_mono_items<'tcx>( } pub fn provide(providers: &mut Providers) { - providers.hooks.should_codegen_locally = should_codegen_locally_hook; + providers.hooks.should_codegen_locally = should_codegen_locally; } diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index d169f08606636..e5a63a98ff47b 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -22,8 +22,6 @@ mod partitioning; mod polymorphize; mod util; -use collector::should_codegen_locally; - rustc_fluent_macro::fluent_messages! { "../messages.ftl" } fn custom_coerce_unsize_info<'tcx>( @@ -73,7 +71,7 @@ pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>( !def_id.is_local() && tcx.is_compiler_builtins(LOCAL_CRATE) && !is_llvm_intrinsic(tcx, def_id) - && !should_codegen_locally(tcx, instance) + && !tcx.should_codegen_locally(instance) } pub fn provide(providers: &mut Providers) { From 9b80250abb8ebf9f49f51593aee6a7308e0ac3b4 Mon Sep 17 00:00:00 2001 From: Mohammad Omidvar Date: Mon, 15 Jul 2024 23:43:52 +0000 Subject: [PATCH 03/18] Move compiler_builtin check to the use case --- .../rustc_codegen_cranelift/src/abi/mod.rs | 2 +- compiler/rustc_codegen_cranelift/src/base.rs | 2 +- compiler/rustc_codegen_cranelift/src/lib.rs | 1 - compiler/rustc_codegen_ssa/src/base.rs | 28 ++++++++++++++++ compiler/rustc_codegen_ssa/src/mir/block.rs | 3 +- compiler/rustc_monomorphize/src/lib.rs | 32 ------------------- 6 files changed, 31 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index fa0de6f9de5ea..698981ae1536d 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -10,12 +10,12 @@ use std::mem; use cranelift_codegen::ir::{ArgumentPurpose, SigRef}; use cranelift_codegen::isa::CallConv; use cranelift_module::ModuleError; +use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization; use rustc_codegen_ssa::errors::CompilerBuiltinsCannotCall; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::ty::layout::FnAbiOf; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::TypeVisitableExt; -use rustc_monomorphize::is_call_from_compiler_builtins_to_upstream_monomorphization; use rustc_session::Session; use rustc_span::source_map::Spanned; use rustc_target::abi::call::{Conv, FnAbi, PassMode}; diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 5adbbb09ac85f..9bc7b57c53745 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -5,13 +5,13 @@ use cranelift_codegen::CodegenError; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use cranelift_module::ModuleError; use rustc_ast::InlineAsmOptions; +use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization; use rustc_index::IndexVec; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::layout::FnAbiOf; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::TypeVisitableExt; -use rustc_monomorphize::is_call_from_compiler_builtins_to_upstream_monomorphization; use crate::constant::ConstantCx; use crate::debuginfo::{FunctionDebugContext, TypeDebugContext}; diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 192e6c91ea38b..8d3d5ac98e1e5 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -24,7 +24,6 @@ extern crate rustc_hir; extern crate rustc_incremental; extern crate rustc_index; extern crate rustc_metadata; -extern crate rustc_monomorphize; extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 137f14fe706cc..399ac4858505e 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -806,6 +806,34 @@ pub fn codegen_crate( ongoing_codegen } +/// Returns whether a call from the current crate to the [`Instance`] would produce a call +/// from `compiler_builtins` to a symbol the linker must resolve. +/// +/// Such calls from `compiler_bultins` are effectively impossible for the linker to handle. Some +/// linkers will optimize such that dead calls to unresolved symbols are not an error, but this is +/// not guaranteed. So we used this function in codegen backends to ensure we do not generate any +/// unlinkable calls. +/// +/// Note that calls to LLVM intrinsics are uniquely okay because they won't make it to the linker. +pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, +) -> bool { + fn is_llvm_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + if let Some(name) = tcx.codegen_fn_attrs(def_id).link_name { + name.as_str().starts_with("llvm.") + } else { + false + } + } + + let def_id = instance.def_id(); + !def_id.is_local() + && tcx.is_compiler_builtins(LOCAL_CRATE) + && !is_llvm_intrinsic(tcx, def_id) + && !tcx.should_codegen_locally(instance) +} + impl CrateInfo { pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> CrateInfo { let crate_types = tcx.crate_types().to_vec(); diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 6a5525dc2b34f..c9c8f02c491bd 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -3,7 +3,7 @@ use super::operand::OperandValue::{Immediate, Pair, Ref, ZeroSized}; use super::place::{PlaceRef, PlaceValue}; use super::{CachedLlbb, FunctionCx, LocalRef}; -use crate::base; +use crate::base::{self, is_call_from_compiler_builtins_to_upstream_monomorphization}; use crate::common::{self, IntPredicate}; use crate::errors::CompilerBuiltinsCannotCall; use crate::meth; @@ -18,7 +18,6 @@ use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Instance, Ty}; use rustc_middle::{bug, span_bug}; -use rustc_monomorphize::is_call_from_compiler_builtins_to_upstream_monomorphization; use rustc_session::config::OptLevel; use rustc_span::{source_map::Spanned, sym, Span}; use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode, Reg}; diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index e5a63a98ff47b..fc6e8e0d14fd2 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -8,12 +8,8 @@ use rustc_middle::bug; use rustc_middle::query::TyCtxtAt; use rustc_middle::traits; use rustc_middle::ty::adjustment::CustomCoerceUnsized; -use rustc_middle::ty::Instance; -use rustc_middle::ty::TyCtxt; use rustc_middle::ty::{self, Ty}; use rustc_middle::util::Providers; -use rustc_span::def_id::DefId; -use rustc_span::def_id::LOCAL_CRATE; use rustc_span::ErrorGuaranteed; mod collector; @@ -46,34 +42,6 @@ fn custom_coerce_unsize_info<'tcx>( } } -/// Returns whether a call from the current crate to the [`Instance`] would produce a call -/// from `compiler_builtins` to a symbol the linker must resolve. -/// -/// Such calls from `compiler_bultins` are effectively impossible for the linker to handle. Some -/// linkers will optimize such that dead calls to unresolved symbols are not an error, but this is -/// not guaranteed. So we used this function in codegen backends to ensure we do not generate any -/// unlinkable calls. -/// -/// Note that calls to LLVM intrinsics are uniquely okay because they won't make it to the linker. -pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>( - tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, -) -> bool { - fn is_llvm_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - if let Some(name) = tcx.codegen_fn_attrs(def_id).link_name { - name.as_str().starts_with("llvm.") - } else { - false - } - } - - let def_id = instance.def_id(); - !def_id.is_local() - && tcx.is_compiler_builtins(LOCAL_CRATE) - && !is_llvm_intrinsic(tcx, def_id) - && !tcx.should_codegen_locally(instance) -} - pub fn provide(providers: &mut Providers) { partitioning::provide(providers); polymorphize::provide(providers); From 2b5a9821eb730ab2e294bc2600af8ee580421e27 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Wed, 17 Jul 2024 18:14:54 +0300 Subject: [PATCH 04/18] abstract merge-base commit logic Signed-off-by: onur-ozkan --- src/bootstrap/src/core/build_steps/llvm.rs | 33 ++++++++------------ src/bootstrap/src/core/config/config.rs | 36 ++++++++++------------ src/bootstrap/src/utils/helpers.rs | 24 +++++++++++++++ src/tools/build_helper/src/git.rs | 2 +- 4 files changed, 54 insertions(+), 41 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 888290a0479ba..b6b8906746206 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -26,7 +26,6 @@ use crate::{generate_smart_stamp_hash, CLang, GitRepo, Kind}; use crate::utils::exec::command; use build_helper::ci::CiEnv; -use build_helper::git::get_git_merge_base; #[derive(Clone)] pub struct LlvmResult { @@ -154,26 +153,18 @@ pub fn prebuilt_llvm_config(builder: &Builder<'_>, target: TargetSelection) -> L /// This retrieves the LLVM sha we *want* to use, according to git history. pub(crate) fn detect_llvm_sha(config: &Config, is_git: bool) -> String { let llvm_sha = if is_git { - // We proceed in 2 steps. First we get the closest commit that is actually upstream. Then we - // walk back further to the last bors merge commit that actually changed LLVM. The first - // step will fail on CI because only the `auto` branch exists; we just fall back to `HEAD` - // in that case. - let closest_upstream = get_git_merge_base(&config.git_config(), Some(&config.src)) - .unwrap_or_else(|_| "HEAD".into()); - let mut rev_list = helpers::git(Some(&config.src)); - rev_list.args(&[ - PathBuf::from("rev-list"), - format!("--author={}", config.stage0_metadata.config.git_merge_commit_email).into(), - "-n1".into(), - "--first-parent".into(), - closest_upstream.into(), - "--".into(), - config.src.join("src/llvm-project"), - config.src.join("src/bootstrap/download-ci-llvm-stamp"), - // the LLVM shared object file is named `LLVM-12-rust-{version}-nightly` - config.src.join("src/version"), - ]); - output(rev_list.as_command_mut()).trim().to_owned() + helpers::get_closest_merge_base_commit( + Some(&config.src), + &config.git_config(), + &config.stage0_metadata.config.git_merge_commit_email, + &[ + config.src.join("src/llvm-project"), + config.src.join("src/bootstrap/download-ci-llvm-stamp"), + // the LLVM shared object file is named `LLVM-12-rust-{version}-nightly` + config.src.join("src/version"), + ], + ) + .unwrap() } else if let Some(info) = channel::read_commit_info_file(&config.src) { info.sha.trim().to_owned() } else { diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index f96633b059a17..9d5aa795c6c0d 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -20,7 +20,7 @@ use crate::core::build_steps::llvm; use crate::core::config::flags::{Color, Flags, Warnings}; use crate::utils::cache::{Interned, INTERNER}; use crate::utils::channel::{self, GitInfo}; -use crate::utils::helpers::{self, exe, output, t}; +use crate::utils::helpers::{self, exe, get_closest_merge_base_commit, output, t}; use build_helper::exit; use serde::{Deserialize, Deserializer}; use serde_derive::Deserialize; @@ -2471,14 +2471,13 @@ impl Config { // Look for a version to compare to based on the current commit. // Only commits merged by bors will have CI artifacts. - let merge_base = output( - helpers::git(Some(&self.src)) - .arg("rev-list") - .arg(format!("--author={}", self.stage0_metadata.config.git_merge_commit_email)) - .args(["-n1", "--first-parent", "HEAD"]) - .as_command_mut(), - ); - let commit = merge_base.trim_end(); + let commit = get_closest_merge_base_commit( + Some(&self.src), + &self.git_config(), + &self.stage0_metadata.config.git_merge_commit_email, + &[], + ) + .unwrap(); if commit.is_empty() { println!("ERROR: could not find commit hash for downloading rustc"); println!("HELP: maybe your repository history is too shallow?"); @@ -2489,7 +2488,7 @@ impl Config { // Warn if there were changes to the compiler or standard library since the ancestor commit. let has_changes = !t!(helpers::git(Some(&self.src)) - .args(["diff-index", "--quiet", commit]) + .args(["diff-index", "--quiet", &commit]) .arg("--") .args([self.src.join("compiler"), self.src.join("library")]) .as_command_mut() @@ -2565,14 +2564,13 @@ impl Config { ) -> Option { // Look for a version to compare to based on the current commit. // Only commits merged by bors will have CI artifacts. - let merge_base = output( - helpers::git(Some(&self.src)) - .arg("rev-list") - .arg(format!("--author={}", self.stage0_metadata.config.git_merge_commit_email)) - .args(["-n1", "--first-parent", "HEAD"]) - .as_command_mut(), - ); - let commit = merge_base.trim_end(); + let commit = get_closest_merge_base_commit( + Some(&self.src), + &self.git_config(), + &self.stage0_metadata.config.git_merge_commit_email, + &[], + ) + .unwrap(); if commit.is_empty() { println!("error: could not find commit hash for downloading components from CI"); println!("help: maybe your repository history is too shallow?"); @@ -2583,7 +2581,7 @@ impl Config { // Warn if there were changes to the compiler or standard library since the ancestor commit. let mut git = helpers::git(Some(&self.src)); - git.args(["diff-index", "--quiet", commit, "--"]); + git.args(["diff-index", "--quiet", &commit, "--"]); // Handle running from a directory other than the top level let top_level = &self.src; diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index 3c82fa189be36..773a873e47cc0 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -3,6 +3,7 @@ //! Simple things like testing the various filesystem operations here and there, //! not a lot of interesting happenings here unfortunately. +use build_helper::git::{get_git_merge_base, output_result, GitConfig}; use build_helper::util::fail; use std::env; use std::ffi::OsStr; @@ -521,3 +522,26 @@ pub fn git(source_dir: Option<&Path>) -> BootstrapCommand { git } + +/// Returns the closest commit available from upstream for the given `author` and `target_paths`. +/// +/// If it fails to find the commit from upstream using `git merge-base`, fallbacks to HEAD. +pub fn get_closest_merge_base_commit( + source_dir: Option<&Path>, + config: &GitConfig<'_>, + author: &str, + target_paths: &[PathBuf], +) -> Result { + let mut git = git(source_dir).capture_stdout(); + + let merge_base = get_git_merge_base(config, source_dir).unwrap_or_else(|_| "HEAD".into()); + + git.arg(Path::new("rev-list")); + git.args([&format!("--author={author}"), "-n1", "--first-parent", &merge_base]); + + if !target_paths.is_empty() { + git.arg("--").args(target_paths); + } + + Ok(output_result(git.as_command_mut())?.trim().to_owned()) +} diff --git a/src/tools/build_helper/src/git.rs b/src/tools/build_helper/src/git.rs index b4522de6897d4..8be38dc855f7a 100644 --- a/src/tools/build_helper/src/git.rs +++ b/src/tools/build_helper/src/git.rs @@ -7,7 +7,7 @@ pub struct GitConfig<'a> { } /// Runs a command and returns the output -fn output_result(cmd: &mut Command) -> Result { +pub fn output_result(cmd: &mut Command) -> Result { let output = match cmd.stderr(Stdio::inherit()).output() { Ok(status) => status, Err(e) => return Err(format!("failed to run command: {:?}: {}", cmd, e)), From 9e354daf7b3ef64740fe2ecf3c5aae9563b68189 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Tue, 16 Jul 2024 19:31:23 -0700 Subject: [PATCH 05/18] unix: Unsafe-wrap stack_overflow::signal_handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sometimes a safety comment is a prayer. avoid fuzzy provenance casts after deref. Co-authored-by: Jonas Böttiger --- library/std/src/sys/pal/unix/stack_overflow.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 2e5bd85327a19..0886624160c08 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -86,13 +86,18 @@ mod imp { // out many large systems and all implementations allow returning from a // signal handler to work. For a more detailed explanation see the // comments on #26458. + /// SIGSEGV/SIGBUS entry point + /// # Safety + /// Rust doesn't call this, it *gets called*. + #[forbid(unsafe_op_in_unsafe_fn)] unsafe extern "C" fn signal_handler( signum: libc::c_int, info: *mut libc::siginfo_t, _data: *mut libc::c_void, ) { let (start, end) = GUARD.get(); - let addr = (*info).si_addr() as usize; + // SAFETY: this pointer is provided by the system and will always point to a valid `siginfo_t`. + let addr = unsafe { (*info).si_addr().addr() }; // If the faulting address is within the guard page, then we print a // message saying so and abort. @@ -104,9 +109,11 @@ mod imp { rtabort!("stack overflow"); } else { // Unregister ourselves by reverting back to the default behavior. - let mut action: sigaction = mem::zeroed(); + // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" + let mut action: sigaction = unsafe { mem::zeroed() }; action.sa_sigaction = SIG_DFL; - sigaction(signum, &action, ptr::null_mut()); + // SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction + unsafe { sigaction(signum, &action, ptr::null_mut()) }; // See comment above for why this function returns. } From 357ba1f8ec0326b9f285224fee8a97691485475f Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Tue, 16 Jul 2024 20:19:16 -0700 Subject: [PATCH 06/18] unix: lift init of sigaltstack before sigaction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is technically "not necessary", as we will "just" segfault instead if we e.g. arrive inside the handler fn with the null altstack. However, it seems incorrect to go about this hoping that segfaulting is okay, seeing as how our purpose here is to mitigate stack overflow problems. Make sure NEED_ALTSTACK syncs with PAGE_SIZE when we do. Co-authored-by: Jonas Böttiger --- .../std/src/sys/pal/unix/stack_overflow.rs | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 0886624160c08..160e58e16a5c4 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -123,28 +123,36 @@ mod imp { static MAIN_ALTSTACK: AtomicPtr = AtomicPtr::new(ptr::null_mut()); static NEED_ALTSTACK: AtomicBool = AtomicBool::new(false); + /// # Safety + /// Must be called only once + #[forbid(unsafe_op_in_unsafe_fn)] pub unsafe fn init() { PAGE_SIZE.store(os::page_size(), Ordering::Relaxed); // Always write to GUARD to ensure the TLS variable is allocated. - let guard = install_main_guard().unwrap_or(0..0); + let guard = unsafe { install_main_guard().unwrap_or(0..0) }; GUARD.set((guard.start, guard.end)); - let mut action: sigaction = mem::zeroed(); + // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" + let mut action: sigaction = unsafe { mem::zeroed() }; for &signal in &[SIGSEGV, SIGBUS] { - sigaction(signal, ptr::null_mut(), &mut action); + // SAFETY: just fetches the current signal handler into action + unsafe { sigaction(signal, ptr::null_mut(), &mut action) }; // Configure our signal handler if one is not already set. if action.sa_sigaction == SIG_DFL { + if !NEED_ALTSTACK.load(Ordering::Relaxed) { + // haven't set up our sigaltstack yet + NEED_ALTSTACK.store(true, Ordering::Release); + let handler = unsafe { make_handler(true) }; + MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed); + mem::forget(handler); + } action.sa_flags = SA_SIGINFO | SA_ONSTACK; action.sa_sigaction = signal_handler as sighandler_t; - sigaction(signal, &action, ptr::null_mut()); - NEED_ALTSTACK.store(true, Ordering::Relaxed); + // SAFETY: only overriding signals if the default is set + unsafe { sigaction(signal, &action, ptr::null_mut()) }; } } - - let handler = make_handler(true); - MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed); - mem::forget(handler); } pub unsafe fn cleanup() { From fa628ceaff4e24fad699cc651cc6e7d521b642a9 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Tue, 16 Jul 2024 21:07:44 -0700 Subject: [PATCH 07/18] unix: Unsafe-wrap stack_overflow::cleanup Editorialize on the wisdom of this as we do. --- library/std/src/sys/pal/unix/stack_overflow.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 160e58e16a5c4..e016f3b3ca4b3 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -155,8 +155,13 @@ mod imp { } } + /// # Safety + /// Must be called only once + #[forbid(unsafe_op_in_unsafe_fn)] pub unsafe fn cleanup() { - drop_handler(MAIN_ALTSTACK.load(Ordering::Relaxed)); + // FIXME: I probably cause more bugs than I'm worth! + // see https://github.com/rust-lang/rust/issues/111272 + unsafe { drop_handler(MAIN_ALTSTACK.load(Ordering::Relaxed)) }; } unsafe fn get_stack() -> libc::stack_t { From c1740eee1ea8dc046f9195d7578d3079ef423f7b Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Tue, 16 Jul 2024 21:08:06 -0700 Subject: [PATCH 08/18] unix: Unsafe-wrap stack_overflow::{drop,make}_handler Note that current_guard is probably not unsafe for future work. --- .../std/src/sys/pal/unix/stack_overflow.rs | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index e016f3b3ca4b3..3944e4e1b48a2 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -206,6 +206,9 @@ mod imp { libc::stack_t { ss_sp: stackp, ss_flags: 0, ss_size: sigstack_size } } + /// # Safety + /// Mutates the alternate signal stack + #[forbid(unsafe_op_in_unsafe_fn)] pub unsafe fn make_handler(main_thread: bool) -> Handler { if !NEED_ALTSTACK.load(Ordering::Relaxed) { return Handler::null(); @@ -213,27 +216,38 @@ mod imp { if !main_thread { // Always write to GUARD to ensure the TLS variable is allocated. - let guard = current_guard().unwrap_or(0..0); + let guard = unsafe { current_guard() }.unwrap_or(0..0); GUARD.set((guard.start, guard.end)); } - let mut stack = mem::zeroed(); - sigaltstack(ptr::null(), &mut stack); + // SAFETY: assuming stack_t is zero-initializable + let mut stack = unsafe { mem::zeroed() }; + // SAFETY: reads current stack_t into stack + unsafe { sigaltstack(ptr::null(), &mut stack) }; // Configure alternate signal stack, if one is not already set. if stack.ss_flags & SS_DISABLE != 0 { - stack = get_stack(); - sigaltstack(&stack, ptr::null_mut()); + // SAFETY: We warned our caller this would happen! + unsafe { + stack = get_stack(); + sigaltstack(&stack, ptr::null_mut()); + } Handler { data: stack.ss_sp as *mut libc::c_void } } else { Handler::null() } } + /// # Safety + /// Must be called + /// - only with our handler or nullptr + /// - only when done with our altstack + /// This disables the alternate signal stack! + #[forbid(unsafe_op_in_unsafe_fn)] pub unsafe fn drop_handler(data: *mut libc::c_void) { if !data.is_null() { let sigstack_size = sigstack_size(); let page_size = PAGE_SIZE.load(Ordering::Relaxed); - let stack = libc::stack_t { + let disabling_stack = libc::stack_t { ss_sp: ptr::null_mut(), ss_flags: SS_DISABLE, // Workaround for bug in macOS implementation of sigaltstack @@ -242,10 +256,11 @@ mod imp { // both ss_sp and ss_size should be ignored in this case. ss_size: sigstack_size, }; - sigaltstack(&stack, ptr::null_mut()); - // We know from `get_stackp` that the alternate stack we installed is part of a mapping - // that started one page earlier, so walk back a page and unmap from there. - munmap(data.sub(page_size), sigstack_size + page_size); + // SAFETY: we warned the caller this disables the alternate signal stack! + unsafe { sigaltstack(&disabling_stack, ptr::null_mut()) }; + // SAFETY: We know from `get_stackp` that the alternate stack we installed is part of + // a mapping that started one page earlier, so walk back a page and unmap from there. + unsafe { munmap(data.sub(page_size), sigstack_size + page_size) }; } } @@ -446,6 +461,7 @@ mod imp { } #[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "solaris"))] + // FIXME: I am probably not unsafe. unsafe fn current_guard() -> Option> { let stackptr = get_stack_start()?; let stackaddr = stackptr.addr(); @@ -460,6 +476,7 @@ mod imp { target_os = "netbsd", target_os = "l4re" ))] + // FIXME: I am probably not unsafe. unsafe fn current_guard() -> Option> { let mut ret = None; let mut attr: libc::pthread_attr_t = crate::mem::zeroed(); From 529fcbcd6d7c72e942f0e70fefb7db182338ee43 Mon Sep 17 00:00:00 2001 From: Jubilee <46493976+workingjubilee@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:24:40 -0700 Subject: [PATCH 09/18] unix: acquire-load NEED_ALTSTACK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jonas Böttiger --- library/std/src/sys/pal/unix/stack_overflow.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 3944e4e1b48a2..047edebb8617b 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -210,7 +210,7 @@ mod imp { /// Mutates the alternate signal stack #[forbid(unsafe_op_in_unsafe_fn)] pub unsafe fn make_handler(main_thread: bool) -> Handler { - if !NEED_ALTSTACK.load(Ordering::Relaxed) { + if !NEED_ALTSTACK.load(Ordering::Acquire) { return Handler::null(); } From 36ebf7a3211d0cb327c01357545346d2a97d54cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= Date: Fri, 19 Jul 2024 11:49:26 +0000 Subject: [PATCH 10/18] run_make_support: skip rustfmt for lib.rs to preserve use ordering --- src/tools/run-make-support/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index e6a45f57de608..b85191970de62 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -3,6 +3,10 @@ //! notably is built via cargo: this means that if your test wants some non-trivial utility, such //! as `object` or `wasmparser`, they can be re-exported and be made available through this library. +// We want to control use declaration ordering and spacing (and preserve use group comments), so +// skip rustfmt on this file. +#![cfg_attr(rustfmt, rustfmt::skip)] + mod command; mod macros; mod util; @@ -18,6 +22,8 @@ pub mod scoped_run; pub mod string; pub mod targets; +// Internally we call our fs-related support module as `fs`, but re-export its content as `rfs` +// to tests to avoid colliding with commonly used `use std::fs;`. mod fs; /// [`std::fs`] wrappers and assorted filesystem-related helpers. Public to tests as `rfs` to not be From 1d83da88473e72717c089a389adcea1c53d4c9f8 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Wed, 17 Jul 2024 12:57:44 -0700 Subject: [PATCH 11/18] kmc-solid: forbid(unsafe_op_in_unsafe_fn) --- library/std/src/os/solid/io.rs | 1 - library/std/src/os/solid/mod.rs | 1 + library/std/src/sys/pal/solid/mod.rs | 2 +- library/std/src/sys/sync/mutex/itron.rs | 1 + library/std/src/sys/sync/rwlock/solid.rs | 1 + 5 files changed, 4 insertions(+), 2 deletions(-) diff --git a/library/std/src/os/solid/io.rs b/library/std/src/os/solid/io.rs index 19b4fe22093c3..e75bcf74e5cd3 100644 --- a/library/std/src/os/solid/io.rs +++ b/library/std/src/os/solid/io.rs @@ -44,7 +44,6 @@ //! //! [`BorrowedFd<'a>`]: crate::os::solid::io::BorrowedFd -#![deny(unsafe_op_in_unsafe_fn)] #![unstable(feature = "solid_ext", issue = "none")] use crate::fmt; diff --git a/library/std/src/os/solid/mod.rs b/library/std/src/os/solid/mod.rs index 0bb83c73ddf7c..75824048e2498 100644 --- a/library/std/src/os/solid/mod.rs +++ b/library/std/src/os/solid/mod.rs @@ -1,4 +1,5 @@ #![stable(feature = "rust1", since = "1.0.0")] +#![forbid(unsafe_op_in_unsafe_fn)] pub mod ffi; pub mod io; diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index 9a7741ddda71e..0b158fb63df70 100644 --- a/library/std/src/sys/pal/solid/mod.rs +++ b/library/std/src/sys/pal/solid/mod.rs @@ -1,6 +1,6 @@ #![allow(dead_code)] #![allow(missing_docs, nonstandard_style)] -#![deny(unsafe_op_in_unsafe_fn)] +#![forbid(unsafe_op_in_unsafe_fn)] pub mod abi; diff --git a/library/std/src/sys/sync/mutex/itron.rs b/library/std/src/sys/sync/mutex/itron.rs index 4ba32a8fbcd69..b29c7e1d03444 100644 --- a/library/std/src/sys/sync/mutex/itron.rs +++ b/library/std/src/sys/sync/mutex/itron.rs @@ -1,5 +1,6 @@ //! Mutex implementation backed by μITRON mutexes. Assumes `acre_mtx` and //! `TA_INHERIT` are available. +#![forbid(unsafe_op_in_unsafe_fn)] use crate::sys::pal::itron::{ abi, diff --git a/library/std/src/sys/sync/rwlock/solid.rs b/library/std/src/sys/sync/rwlock/solid.rs index 7558eee8edd33..a8fef685ceb27 100644 --- a/library/std/src/sys/sync/rwlock/solid.rs +++ b/library/std/src/sys/sync/rwlock/solid.rs @@ -1,4 +1,5 @@ //! A readers-writer lock implementation backed by the SOLID kernel extension. +#![forbid(unsafe_op_in_unsafe_fn)] use crate::sys::pal::{ abi, From e9b3e9c7f44949abd71ac05ea9d46b3c418888e5 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Wed, 17 Jul 2024 13:10:12 -0700 Subject: [PATCH 12/18] std: forbid unwrapped unsafe in unsupported_backslash --- library/std/src/sys/path/unsupported_backslash.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/src/sys/path/unsupported_backslash.rs b/library/std/src/sys/path/unsupported_backslash.rs index 7045c9be25b08..855f443678c6c 100644 --- a/library/std/src/sys/path/unsupported_backslash.rs +++ b/library/std/src/sys/path/unsupported_backslash.rs @@ -1,3 +1,4 @@ +#![forbid(unsafe_op_in_unsafe_fn)] use crate::ffi::OsStr; use crate::io; use crate::path::{Path, PathBuf, Prefix}; From 3b7e4bc77a83c1101e8799739504b0a53f0d0619 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 18 Jul 2024 20:27:15 +1000 Subject: [PATCH 13/18] Split out `remove_never_subcandidates` --- .../rustc_mir_build/src/build/matches/mod.rs | 84 +++++++++++-------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index b531a392efac9..6bd2c308128ad 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -1706,6 +1706,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } self.merge_trivial_subcandidates(candidate); + self.remove_never_subcandidates(candidate); if !candidate.match_pairs.is_empty() { let or_span = candidate.or_span.unwrap_or(candidate.extra_data.span); @@ -1753,44 +1754,53 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let can_merge = candidate.subcandidates.iter().all(|subcandidate| { subcandidate.subcandidates.is_empty() && subcandidate.extra_data.is_empty() }); - if can_merge { - let mut last_otherwise = None; - let any_matches = self.cfg.start_new_block(); - let or_span = candidate.or_span.take().unwrap(); - let source_info = self.source_info(or_span); - if candidate.false_edge_start_block.is_none() { - candidate.false_edge_start_block = - candidate.subcandidates[0].false_edge_start_block; - } - for subcandidate in mem::take(&mut candidate.subcandidates) { - let or_block = subcandidate.pre_binding_block.unwrap(); - self.cfg.goto(or_block, source_info, any_matches); - last_otherwise = subcandidate.otherwise_block; - } - candidate.pre_binding_block = Some(any_matches); - assert!(last_otherwise.is_some()); - candidate.otherwise_block = last_otherwise; - } else { - // Never subcandidates may have a set of bindings inconsistent with their siblings, - // which would break later code. So we filter them out. Note that we can't filter out - // top-level candidates this way. - candidate.subcandidates.retain_mut(|candidate| { - if candidate.extra_data.is_never { - candidate.visit_leaves(|subcandidate| { - let block = subcandidate.pre_binding_block.unwrap(); - // That block is already unreachable but needs a terminator to make the MIR well-formed. - let source_info = self.source_info(subcandidate.extra_data.span); - self.cfg.terminate(block, source_info, TerminatorKind::Unreachable); - }); - false - } else { - true - } - }); - if candidate.subcandidates.is_empty() { - // If `candidate` has become a leaf candidate, ensure it has a `pre_binding_block`. - candidate.pre_binding_block = Some(self.cfg.start_new_block()); + if !can_merge { + return; + } + + let mut last_otherwise = None; + let any_matches = self.cfg.start_new_block(); + let or_span = candidate.or_span.take().unwrap(); + let source_info = self.source_info(or_span); + + if candidate.false_edge_start_block.is_none() { + candidate.false_edge_start_block = candidate.subcandidates[0].false_edge_start_block; + } + + for subcandidate in mem::take(&mut candidate.subcandidates) { + let or_block = subcandidate.pre_binding_block.unwrap(); + self.cfg.goto(or_block, source_info, any_matches); + last_otherwise = subcandidate.otherwise_block; + } + candidate.pre_binding_block = Some(any_matches); + assert!(last_otherwise.is_some()); + candidate.otherwise_block = last_otherwise; + } + + /// Never subcandidates may have a set of bindings inconsistent with their siblings, + /// which would break later code. So we filter them out. Note that we can't filter out + /// top-level candidates this way. + fn remove_never_subcandidates(&mut self, candidate: &mut Candidate<'_, 'tcx>) { + if candidate.subcandidates.is_empty() { + return; + } + + candidate.subcandidates.retain_mut(|candidate| { + if candidate.extra_data.is_never { + candidate.visit_leaves(|subcandidate| { + let block = subcandidate.pre_binding_block.unwrap(); + // That block is already unreachable but needs a terminator to make the MIR well-formed. + let source_info = self.source_info(subcandidate.extra_data.span); + self.cfg.terminate(block, source_info, TerminatorKind::Unreachable); + }); + false + } else { + true } + }); + if candidate.subcandidates.is_empty() { + // If `candidate` has become a leaf candidate, ensure it has a `pre_binding_block`. + candidate.pre_binding_block = Some(self.cfg.start_new_block()); } } From e091c356fa0e47251f6c7d11cbc7043213df48c6 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 18 Jul 2024 20:58:52 +1000 Subject: [PATCH 14/18] Improve `merge_trivial_subcandidates` --- .../rustc_mir_build/src/build/matches/mod.rs | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 6bd2c308128ad..d18e59759e203 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -1744,8 +1744,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Try to merge all of the subcandidates of the given candidate into one. This avoids /// exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. The candidate should have been /// expanded with `create_or_subcandidates`. + /// + /// Note that this takes place _after_ the subcandidates have participated + /// in match tree lowering. fn merge_trivial_subcandidates(&mut self, candidate: &mut Candidate<'_, 'tcx>) { - if candidate.subcandidates.is_empty() || candidate.has_guard { + assert!(!candidate.subcandidates.is_empty()); + if candidate.has_guard { // FIXME(or_patterns; matthewjasper) Don't give up if we have a guard. return; } @@ -1759,7 +1763,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } let mut last_otherwise = None; - let any_matches = self.cfg.start_new_block(); + let shared_pre_binding_block = self.cfg.start_new_block(); + // This candidate is about to become a leaf, so unset `or_span`. let or_span = candidate.or_span.take().unwrap(); let source_info = self.source_info(or_span); @@ -1767,12 +1772,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidate.false_edge_start_block = candidate.subcandidates[0].false_edge_start_block; } + // Remove the (known-trivial) subcandidates from the candidate tree, + // so that they aren't visible after match tree lowering, and wire them + // all to join up at a single shared pre-binding block. + // (Note that the subcandidates have already had their part of the match + // tree lowered by this point, which is why we can add a goto to them.) for subcandidate in mem::take(&mut candidate.subcandidates) { - let or_block = subcandidate.pre_binding_block.unwrap(); - self.cfg.goto(or_block, source_info, any_matches); + let subcandidate_block = subcandidate.pre_binding_block.unwrap(); + self.cfg.goto(subcandidate_block, source_info, shared_pre_binding_block); last_otherwise = subcandidate.otherwise_block; } - candidate.pre_binding_block = Some(any_matches); + candidate.pre_binding_block = Some(shared_pre_binding_block); assert!(last_otherwise.is_some()); candidate.otherwise_block = last_otherwise; } From e60c5c1a77a4bc6a2540437969ec0dc6e213d712 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 18 Jul 2024 22:11:09 +1000 Subject: [PATCH 15/18] Split out `test_remaining_match_pairs_after_or` Only the last candidate can possibly have more match pairs, so this can be separate from the main or-candidate postprocessing loop. --- .../rustc_mir_build/src/build/matches/mod.rs | 101 ++++++++++-------- 1 file changed, 58 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index d18e59759e203..8bba932908514 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -1598,6 +1598,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { for subcandidate in candidate.subcandidates.iter_mut() { expanded_candidates.push(subcandidate); } + // Note that the subcandidates have been added to `expanded_candidates`, + // but `candidate` itself has not. If the last candidate has more match pairs, + // they are handled separately by `test_remaining_match_pairs_after_or`. } else { // A candidate that doesn't start with an or-pattern has nothing to // expand, so it is included in the post-expansion list as-is. @@ -1613,12 +1616,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expanded_candidates.as_mut_slice(), ); - // Simplify subcandidates and process any leftover match pairs. - for candidate in candidates_to_expand { + // Postprocess subcandidates, and process any leftover match pairs. + // (Only the last candidate can possibly have more match pairs.) + debug_assert!({ + let mut all_except_last = candidates_to_expand.iter().rev().skip(1); + all_except_last.all(|candidate| candidate.match_pairs.is_empty()) + }); + for candidate in candidates_to_expand.iter_mut() { if !candidate.subcandidates.is_empty() { - self.finalize_or_candidate(span, scrutinee_span, candidate); + self.finalize_or_candidate(candidate); } } + if let Some(last_candidate) = candidates_to_expand.last_mut() { + self.test_remaining_match_pairs_after_or(span, scrutinee_span, last_candidate); + } remainder_start.and(remaining_candidates) } @@ -1642,8 +1653,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidate.subcandidates[0].false_edge_start_block = candidate.false_edge_start_block; } - /// Simplify subcandidates and process any leftover match pairs. The candidate should have been - /// expanded with `create_or_subcandidates`. + /// Simplify subcandidates and remove `is_never` subcandidates. + /// The candidate should have been expanded with `create_or_subcandidates`. /// /// Given a pattern `(P | Q, R | S)` we (in principle) generate a CFG like /// so: @@ -1695,50 +1706,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// | /// ... /// ``` - fn finalize_or_candidate( - &mut self, - span: Span, - scrutinee_span: Span, - candidate: &mut Candidate<'_, 'tcx>, - ) { + fn finalize_or_candidate(&mut self, candidate: &mut Candidate<'_, 'tcx>) { if candidate.subcandidates.is_empty() { return; } self.merge_trivial_subcandidates(candidate); self.remove_never_subcandidates(candidate); - - if !candidate.match_pairs.is_empty() { - let or_span = candidate.or_span.unwrap_or(candidate.extra_data.span); - let source_info = self.source_info(or_span); - // If more match pairs remain, test them after each subcandidate. - // We could add them to the or-candidates before the call to `test_or_pattern` but this - // would make it impossible to detect simplifiable or-patterns. That would guarantee - // exponentially large CFGs for cases like `(1 | 2, 3 | 4, ...)`. - let mut last_otherwise = None; - candidate.visit_leaves(|leaf_candidate| { - last_otherwise = leaf_candidate.otherwise_block; - }); - let remaining_match_pairs = mem::take(&mut candidate.match_pairs); - candidate.visit_leaves(|leaf_candidate| { - assert!(leaf_candidate.match_pairs.is_empty()); - leaf_candidate.match_pairs.extend(remaining_match_pairs.iter().cloned()); - let or_start = leaf_candidate.pre_binding_block.unwrap(); - let otherwise = - self.match_candidates(span, scrutinee_span, or_start, &mut [leaf_candidate]); - // In a case like `(P | Q, R | S)`, if `P` succeeds and `R | S` fails, we know `(Q, - // R | S)` will fail too. If there is no guard, we skip testing of `Q` by branching - // directly to `last_otherwise`. If there is a guard, - // `leaf_candidate.otherwise_block` can be reached by guard failure as well, so we - // can't skip `Q`. - let or_otherwise = if leaf_candidate.has_guard { - leaf_candidate.otherwise_block.unwrap() - } else { - last_otherwise.unwrap() - }; - self.cfg.goto(otherwise, source_info, or_otherwise); - }); - } } /// Try to merge all of the subcandidates of the given candidate into one. This avoids @@ -1814,6 +1788,47 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + /// If more match pairs remain, test them after each subcandidate. + /// We could have added them to the or-candidates during or-pattern expansion, but that + /// would make it impossible to detect simplifiable or-patterns. That would guarantee + /// exponentially large CFGs for cases like `(1 | 2, 3 | 4, ...)`. + fn test_remaining_match_pairs_after_or( + &mut self, + span: Span, + scrutinee_span: Span, + candidate: &mut Candidate<'_, 'tcx>, + ) { + if candidate.match_pairs.is_empty() { + return; + } + + let or_span = candidate.or_span.unwrap_or(candidate.extra_data.span); + let source_info = self.source_info(or_span); + let mut last_otherwise = None; + candidate.visit_leaves(|leaf_candidate| { + last_otherwise = leaf_candidate.otherwise_block; + }); + let remaining_match_pairs = mem::take(&mut candidate.match_pairs); + candidate.visit_leaves(|leaf_candidate| { + assert!(leaf_candidate.match_pairs.is_empty()); + leaf_candidate.match_pairs.extend(remaining_match_pairs.iter().cloned()); + let or_start = leaf_candidate.pre_binding_block.unwrap(); + let otherwise = + self.match_candidates(span, scrutinee_span, or_start, &mut [leaf_candidate]); + // In a case like `(P | Q, R | S)`, if `P` succeeds and `R | S` fails, we know `(Q, + // R | S)` will fail too. If there is no guard, we skip testing of `Q` by branching + // directly to `last_otherwise`. If there is a guard, + // `leaf_candidate.otherwise_block` can be reached by guard failure as well, so we + // can't skip `Q`. + let or_otherwise = if leaf_candidate.has_guard { + leaf_candidate.otherwise_block.unwrap() + } else { + last_otherwise.unwrap() + }; + self.cfg.goto(otherwise, source_info, or_otherwise); + }); + } + /// Pick a test to run. Which test doesn't matter as long as it is guaranteed to fully match at /// least one match pair. We currently simply pick the test corresponding to the first match /// pair of the first candidate in the list. From 886668cc2ea22a3d52f775961bac06d9499d9e26 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 18 Jul 2024 22:34:49 +1000 Subject: [PATCH 16/18] Improve `test_remaining_match_pairs_after_or` --- compiler/rustc_mir_build/src/build/matches/mod.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 8bba932908514..d8db08bfb8af5 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -1808,10 +1808,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidate.visit_leaves(|leaf_candidate| { last_otherwise = leaf_candidate.otherwise_block; }); + let remaining_match_pairs = mem::take(&mut candidate.match_pairs); + // We're testing match pairs that remained after an `Or`, so the remaining + // pairs should all be `Or` too, due to the sorting invariant. + debug_assert!( + remaining_match_pairs + .iter() + .all(|match_pair| matches!(match_pair.test_case, TestCase::Or { .. })) + ); + candidate.visit_leaves(|leaf_candidate| { + // At this point the leaf's own match pairs have all been lowered + // and removed, so `extend` and assignment are equivalent, + // but extending can also recycle any existing vector capacity. assert!(leaf_candidate.match_pairs.is_empty()); leaf_candidate.match_pairs.extend(remaining_match_pairs.iter().cloned()); + let or_start = leaf_candidate.pre_binding_block.unwrap(); let otherwise = self.match_candidates(span, scrutinee_span, or_start, &mut [leaf_candidate]); From 239037ecde3d884ad09bfb95c950021f3bc78da1 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 18 Jul 2024 22:51:17 +1000 Subject: [PATCH 17/18] Inline `finalize_or_candidate` --- .../rustc_mir_build/src/build/matches/mod.rs | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index d8db08bfb8af5..db7aed4306b20 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -1624,7 +1624,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }); for candidate in candidates_to_expand.iter_mut() { if !candidate.subcandidates.is_empty() { - self.finalize_or_candidate(candidate); + self.merge_trivial_subcandidates(candidate); + self.remove_never_subcandidates(candidate); } } if let Some(last_candidate) = candidates_to_expand.last_mut() { @@ -1635,8 +1636,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } /// Given a match-pair that corresponds to an or-pattern, expand each subpattern into a new - /// subcandidate. Any candidate that has been expanded that way should be passed to - /// `finalize_or_candidate` after its subcandidates have been processed. + /// subcandidate. Any candidate that has been expanded this way should also be postprocessed + /// at the end of [`Self::expand_and_match_or_candidates`]. fn create_or_subcandidates<'pat>( &mut self, candidate: &mut Candidate<'pat, 'tcx>, @@ -1653,8 +1654,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidate.subcandidates[0].false_edge_start_block = candidate.false_edge_start_block; } - /// Simplify subcandidates and remove `is_never` subcandidates. - /// The candidate should have been expanded with `create_or_subcandidates`. + /// Try to merge all of the subcandidates of the given candidate into one. This avoids + /// exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. The candidate should have been + /// expanded with `create_or_subcandidates`. /// /// Given a pattern `(P | Q, R | S)` we (in principle) generate a CFG like /// so: @@ -1706,18 +1708,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// | /// ... /// ``` - fn finalize_or_candidate(&mut self, candidate: &mut Candidate<'_, 'tcx>) { - if candidate.subcandidates.is_empty() { - return; - } - - self.merge_trivial_subcandidates(candidate); - self.remove_never_subcandidates(candidate); - } - - /// Try to merge all of the subcandidates of the given candidate into one. This avoids - /// exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. The candidate should have been - /// expanded with `create_or_subcandidates`. /// /// Note that this takes place _after_ the subcandidates have participated /// in match tree lowering. From 0636293fb740c96e7a52cf53452b728b1cf9e834 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Sun, 7 Jul 2024 19:02:32 +0300 Subject: [PATCH 18/18] use precompiled rustdoc with CI rustc When CI rustc is enabled and rustdoc sources are unchanged, we can use the precompiled rustdoc from the CI rustc's sysroot. This speeds up bootstrapping quite a lot by avoiding unnecessary rustdoc compilation. Signed-off-by: onur-ozkan --- src/bootstrap/src/core/build_steps/tool.rs | 59 +++++++++++++++++++--- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 7bc410b9e8878..459e59e1b3446 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -9,7 +9,7 @@ use crate::core::builder::{Builder, Cargo as CargoCommand, RunConfig, ShouldRun, use crate::core::config::TargetSelection; use crate::utils::channel::GitInfo; use crate::utils::exec::{command, BootstrapCommand}; -use crate::utils::helpers::{add_dylib_path, exe, t}; +use crate::utils::helpers::{add_dylib_path, exe, get_closest_merge_base_commit, git, t}; use crate::Compiler; use crate::Mode; use crate::{gha, Kind}; @@ -554,6 +554,57 @@ impl Step for Rustdoc { } let target = target_compiler.host; + let bin_rustdoc = || { + let sysroot = builder.sysroot(target_compiler); + let bindir = sysroot.join("bin"); + t!(fs::create_dir_all(&bindir)); + let bin_rustdoc = bindir.join(exe("rustdoc", target_compiler.host)); + let _ = fs::remove_file(&bin_rustdoc); + bin_rustdoc + }; + + // If CI rustc is enabled and we haven't modified the rustdoc sources, + // use the precompiled rustdoc from CI rustc's sysroot to speed up bootstrapping. + if builder.download_rustc() + && target_compiler.stage > 0 + && builder.rust_info().is_managed_git_subrepository() + { + let commit = get_closest_merge_base_commit( + Some(&builder.config.src), + &builder.config.git_config(), + &builder.config.stage0_metadata.config.git_merge_commit_email, + &[], + ) + .unwrap(); + + let librustdoc_src = builder.config.src.join("src/librustdoc"); + let rustdoc_src = builder.config.src.join("src/tools/rustdoc"); + + // FIXME: The change detection logic here is quite similar to `Config::download_ci_rustc_commit`. + // It would be better to unify them. + let has_changes = !git(Some(&builder.config.src)) + .allow_failure() + .run_always() + .args(["diff-index", "--quiet", &commit]) + .arg("--") + .arg(librustdoc_src) + .arg(rustdoc_src) + .run(builder) + .is_success(); + + if !has_changes { + let precompiled_rustdoc = builder + .config + .ci_rustc_dir() + .join("bin") + .join(exe("rustdoc", target_compiler.host)); + + let bin_rustdoc = bin_rustdoc(); + builder.copy_link(&precompiled_rustdoc, &bin_rustdoc); + return bin_rustdoc; + } + } + let build_compiler = if builder.download_rustc() && target_compiler.stage == 1 { // We already have the stage 1 compiler, we don't need to cut the stage. builder.compiler(target_compiler.stage, builder.config.build) @@ -614,11 +665,7 @@ impl Step for Rustdoc { // don't create a stage0-sysroot/bin directory. if target_compiler.stage > 0 { - let sysroot = builder.sysroot(target_compiler); - let bindir = sysroot.join("bin"); - t!(fs::create_dir_all(&bindir)); - let bin_rustdoc = bindir.join(exe("rustdoc", target_compiler.host)); - let _ = fs::remove_file(&bin_rustdoc); + let bin_rustdoc = bin_rustdoc(); builder.copy_link(&tool_rustdoc, &bin_rustdoc); bin_rustdoc } else {