From 5f4f2526b84e585842bc94c74a56588ec0154c10 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Fri, 15 Mar 2024 14:13:11 -0400 Subject: [PATCH 1/3] Handle calls to upstream monomorphizations in compiler_builtins --- Cargo.lock | 1 + .../rustc_codegen_cranelift/src/abi/mod.rs | 14 +++++++ compiler/rustc_codegen_cranelift/src/base.rs | 7 ++++ compiler/rustc_codegen_cranelift/src/lib.rs | 1 + compiler/rustc_codegen_ssa/Cargo.toml | 1 + compiler/rustc_codegen_ssa/messages.ftl | 3 ++ compiler/rustc_codegen_ssa/src/common.rs | 5 ++- compiler/rustc_codegen_ssa/src/errors.rs | 7 ++++ compiler/rustc_codegen_ssa/src/mir/block.rs | 39 ++++++++++++++++--- compiler/rustc_codegen_ssa/src/size_of_val.rs | 3 +- compiler/rustc_monomorphize/src/collector.rs | 2 +- compiler/rustc_monomorphize/src/lib.rs | 21 ++++++++++ 12 files changed, 95 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 16aed3dc49c..91a6ebe4446 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3673,6 +3673,7 @@ dependencies = [ "rustc_macros", "rustc_metadata", "rustc_middle", + "rustc_monomorphize", "rustc_query_system", "rustc_serialize", "rustc_session", diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 6e846d721f2..b33587f8948 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -8,8 +8,11 @@ use std::borrow::Cow; use cranelift_codegen::ir::SigRef; use cranelift_module::ModuleError; +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_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}; @@ -372,6 +375,17 @@ pub(crate) fn codegen_terminator_call<'tcx>( ty::Instance::expect_resolve(fx.tcx, ty::ParamEnv::reveal_all(), def_id, fn_args) .polymorphize(fx.tcx); + if is_call_from_compiler_builtins_to_upstream_monomorphization(fx.tcx, instance) { + if target.is_some() { + let caller = with_no_trimmed_paths!(fx.tcx.def_path_str(fx.instance.def_id())); + let callee = with_no_trimmed_paths!(fx.tcx.def_path_str(def_id)); + fx.tcx.dcx().emit_err(CompilerBuiltinsCannotCall { caller, callee }); + } else { + fx.bcx.ins().trap(TrapCode::User(0)); + return; + } + } + if fx.tcx.symbol_name(instance).name.starts_with("llvm.") { crate::intrinsics::codegen_llvm_intrinsic_call( fx, diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 2415c2c90b2..047dc56a32e 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -8,6 +8,7 @@ use rustc_index::IndexVec; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::layout::FnAbiOf; use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_monomorphize::is_call_from_compiler_builtins_to_upstream_monomorphization; use crate::constant::ConstantCx; use crate::debuginfo::FunctionDebugContext; @@ -999,6 +1000,12 @@ fn codegen_panic_inner<'tcx>( let def_id = fx.tcx.require_lang_item(lang_item, span); let instance = Instance::mono(fx.tcx, def_id).polymorphize(fx.tcx); + + if is_call_from_compiler_builtins_to_upstream_monomorphization(fx.tcx, instance) { + fx.bcx.ins().trap(TrapCode::User(0)); + return; + } + let symbol_name = fx.tcx.symbol_name(instance).name; fx.lib_call( diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 7e2e1f7c6ac..a59a39074f8 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -21,6 +21,7 @@ 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/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 7851b9e8e03..baf10622a6d 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -25,6 +25,7 @@ rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_metadata = { path = "../rustc_metadata" } rustc_middle = { path = "../rustc_middle" } +rustc_monomorphize = { path = "../rustc_monomorphize" } rustc_query_system = { path = "../rustc_query_system" } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 5ba66d1be43..d159fe58d3e 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -16,6 +16,9 @@ codegen_ssa_cgu_not_recorded = codegen_ssa_check_installed_visual_studio = please ensure that Visual Studio 2017 or later, or Build Tools for Visual Studio were installed with the Visual C++ option. +codegen_ssa_compiler_builtins_cannot_call = + `compiler_builtins` cannot call functions through upstream monomorphizations; encountered invalid call from `{$caller}` to `{$callee}` + codegen_ssa_copy_path = could not copy {$from} to {$to}: {$error} codegen_ssa_copy_path_buf = unable to copy {$source_file} to {$output_path}: {$error} diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index 44a2434238d..71fca403def 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -2,6 +2,7 @@ use rustc_hir::LangItem; use rustc_middle::mir; +use rustc_middle::ty::Instance; use rustc_middle::ty::{self, layout::TyAndLayout, Ty, TyCtxt}; use rustc_span::Span; @@ -120,11 +121,11 @@ pub fn build_langcall<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &Bx, span: Option, li: LangItem, -) -> (Bx::FnAbiOfResult, Bx::Value) { +) -> (Bx::FnAbiOfResult, Bx::Value, Instance<'tcx>) { let tcx = bx.tcx(); let def_id = tcx.require_lang_item(li, span); let instance = ty::Instance::mono(tcx, def_id); - (bx.fn_abi_of_instance(instance, ty::List::empty()), bx.get_fn_addr(instance)) + (bx.fn_abi_of_instance(instance, ty::List::empty()), bx.get_fn_addr(instance), instance) } // To avoid UB from LLVM, these two functions mask RHS with an diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 3572ee301c8..b843d1bdf23 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1030,3 +1030,10 @@ pub struct FailedToGetLayout<'tcx> { pub struct ErrorCreatingRemarkDir { pub error: std::io::Error, } + +#[derive(Diagnostic)] +#[diag(codegen_ssa_compiler_builtins_cannot_call)] +pub struct CompilerBuiltinsCannotCall { + pub caller: String, + pub callee: String, +} diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 9bb2a528265..09286a8ed24 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -5,6 +5,7 @@ use super::{CachedLlbb, FunctionCx, LocalRef}; use crate::base; use crate::common::{self, IntPredicate}; +use crate::errors::CompilerBuiltinsCannotCall; use crate::meth; use crate::traits::*; use crate::MemFlags; @@ -16,6 +17,7 @@ use rustc_middle::mir::{self, AssertKind, BasicBlock, SwitchTargets, UnwindTermi 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_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}; @@ -157,8 +159,28 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { destination: Option<(ReturnDest<'tcx, Bx::Value>, mir::BasicBlock)>, mut unwind: mir::UnwindAction, copied_constant_arguments: &[PlaceRef<'tcx, ::Value>], + instance: Option>, mergeable_succ: bool, ) -> MergingSucc { + let tcx = bx.tcx(); + if let Some(instance) = instance { + if is_call_from_compiler_builtins_to_upstream_monomorphization(tcx, instance) { + if destination.is_some() { + let caller = with_no_trimmed_paths!(tcx.def_path_str(fx.instance.def_id())); + let callee = with_no_trimmed_paths!(tcx.def_path_str(instance.def_id())); + tcx.dcx().emit_err(CompilerBuiltinsCannotCall { caller, callee }); + } else { + info!( + "compiler_builtins call to diverging function {:?} replaced with abort", + instance.def_id() + ); + bx.abort(); + bx.unreachable(); + return MergingSucc::False; + } + } + } + // If there is a cleanup block and the function we're calling can unwind, then // do an invoke, otherwise do a call. let fn_ty = bx.fn_decl_backend_type(fn_abi); @@ -480,6 +502,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let ty = location.ty(self.mir, bx.tcx()).ty; let ty = self.monomorphize(ty); let drop_fn = Instance::resolve_drop_in_place(bx.tcx(), ty); + let instance = drop_fn.clone(); if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def { // we don't actually need to drop anything. @@ -582,6 +605,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { Some((ReturnDest::Nothing, target)), unwind, &[], + Some(instance), mergeable_succ, ) } @@ -658,10 +682,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } }; - let (fn_abi, llfn) = common::build_langcall(bx, Some(span), lang_item); + let (fn_abi, llfn, instance) = common::build_langcall(bx, Some(span), lang_item); // Codegen the actual panic invoke/call. - let merging_succ = helper.do_call(self, bx, fn_abi, llfn, &args, None, unwind, &[], false); + let merging_succ = + helper.do_call(self, bx, fn_abi, llfn, &args, None, unwind, &[], Some(instance), false); assert_eq!(merging_succ, MergingSucc::False); MergingSucc::False } @@ -677,7 +702,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.set_debug_loc(bx, terminator.source_info); // Obtain the panic entry point. - let (fn_abi, llfn) = common::build_langcall(bx, Some(span), reason.lang_item()); + let (fn_abi, llfn, instance) = common::build_langcall(bx, Some(span), reason.lang_item()); // Codegen the actual panic invoke/call. let merging_succ = helper.do_call( @@ -689,6 +714,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { None, mir::UnwindAction::Unreachable, &[], + Some(instance), false, ); assert_eq!(merging_succ, MergingSucc::False); @@ -738,7 +764,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let msg = bx.const_str(&msg_str); // Obtain the panic entry point. - let (fn_abi, llfn) = + let (fn_abi, llfn, instance) = common::build_langcall(bx, Some(source_info.span), LangItem::PanicNounwind); // Codegen the actual panic invoke/call. @@ -751,6 +777,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { target.as_ref().map(|bb| (ReturnDest::Nothing, *bb)), unwind, &[], + Some(instance), mergeable_succ, ) } else { @@ -798,6 +825,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ty::FnPtr(_) => (None, Some(callee.immediate())), _ => bug!("{} is not callable", callee.layout.ty), }; + let def = instance.map(|i| i.def); if let Some(ty::InstanceDef::DropGlue(_, None)) = def { @@ -1106,6 +1134,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { destination, unwind, &copied_constant_arguments, + instance, mergeable_succ, ) } @@ -1664,7 +1693,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.set_debug_loc(&mut bx, mir::SourceInfo::outermost(self.mir.span)); - let (fn_abi, fn_ptr) = common::build_langcall(&bx, None, reason.lang_item()); + let (fn_abi, fn_ptr, _instance) = common::build_langcall(&bx, None, reason.lang_item()); let fn_ty = bx.fn_decl_backend_type(fn_abi); let llret = bx.call(fn_ty, None, Some(fn_abi), fn_ptr, &[], funclet.as_ref()); diff --git a/compiler/rustc_codegen_ssa/src/size_of_val.rs b/compiler/rustc_codegen_ssa/src/size_of_val.rs index 087836ca37d..e2e95cede60 100644 --- a/compiler/rustc_codegen_ssa/src/size_of_val.rs +++ b/compiler/rustc_codegen_ssa/src/size_of_val.rs @@ -62,7 +62,8 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let msg = bx.const_str(&msg_str); // Obtain the panic entry point. - let (fn_abi, llfn) = common::build_langcall(bx, None, LangItem::PanicNounwind); + let (fn_abi, llfn, _instance) = + common::build_langcall(bx, None, LangItem::PanicNounwind); // Generate the call. // Cannot use `do_call` since we don't have a MIR terminator so we can't create a `TerminationCodegenHelper`. diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index cd9eb4916ce..cf4a62a5edf 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1020,7 +1020,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. -fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) -> bool { +pub(crate) fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) -> bool { let Some(def_id) = instance.def.def_id_if_not_guaranteed_local_codegen() else { return true; }; diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 7f36ae91f1a..6de54541643 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -11,7 +11,10 @@ use rustc_hir::lang_items::LangItem; use rustc_middle::query::{Providers, 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_span::def_id::LOCAL_CRATE; use rustc_span::ErrorGuaranteed; mod collector; @@ -20,6 +23,8 @@ mod partitioning; mod polymorphize; mod util; +use collector::should_codegen_locally; + rustc_fluent_macro::fluent_messages! { "../messages.ftl" } fn custom_coerce_unsize_info<'tcx>( @@ -45,6 +50,22 @@ 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. +pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, +) -> bool { + !instance.def_id().is_local() + && tcx.is_compiler_builtins(LOCAL_CRATE) + && !should_codegen_locally(tcx, &instance) +} + pub fn provide(providers: &mut Providers) { partitioning::provide(providers); polymorphize::provide(providers); From 82717ab877099db6854c6176de90943c14393f6d Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Tue, 19 Mar 2024 13:18:23 -0400 Subject: [PATCH 2/3] Account for #[link_name] intrinsics shims --- compiler/rustc_monomorphize/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 6de54541643..4ec842e8f85 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -63,6 +63,7 @@ pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>( ) -> bool { !instance.def_id().is_local() && tcx.is_compiler_builtins(LOCAL_CRATE) + && tcx.codegen_fn_attrs(instance.def_id()).link_name.is_none() && !should_codegen_locally(tcx, &instance) } From 2f6fb234de5a12fd314ec91737c1f3079f023d8c Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Mon, 18 Mar 2024 18:47:41 -0400 Subject: [PATCH 3/3] Add a test --- Cargo.lock | 39 ++++-- Cargo.toml | 9 -- compiler/rustc_codegen_ssa/src/mir/block.rs | 12 +- compiler/rustc_middle/src/ty/instance.rs | 6 + src/tools/run-make-support/Cargo.toml | 1 + src/tools/run-make-support/src/lib.rs | 1 + tests/run-make/compiler-builtins/rmake.rs | 142 ++++++++++++++++++++ 7 files changed, 189 insertions(+), 21 deletions(-) create mode 100644 tests/run-make/compiler-builtins/rmake.rs diff --git a/Cargo.lock b/Cargo.lock index 91a6ebe4446..29ebd2b3979 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -214,7 +214,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9792d37ca5173d7e7f4fe453739a0671d0557915a030a383d6b866476bbc3e71" dependencies = [ - "object", + "object 0.32.2", ] [[package]] @@ -281,7 +281,7 @@ dependencies = [ "cfg-if", "libc", "miniz_oxide", - "object", + "object 0.32.2", "rustc-demangle", ] @@ -2623,10 +2623,21 @@ dependencies = [ "memchr", "rustc-std-workspace-alloc", "rustc-std-workspace-core", - "ruzstd", + "ruzstd 0.5.0", "wasmparser", ] +[[package]] +name = "object" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7090bae93f8585aad99e595b7073c5de9ba89fbd6b4e9f0cdd7a10177273ac8" +dependencies = [ + "flate2", + "memchr", + "ruzstd 0.6.0", +] + [[package]] name = "odht" version = "0.3.1" @@ -3310,6 +3321,7 @@ dependencies = [ name = "run_make_support" version = "0.0.0" dependencies = [ + "object 0.34.0", "wasmparser", ] @@ -3621,7 +3633,7 @@ dependencies = [ "itertools 0.12.1", "libc", "measureme", - "object", + "object 0.32.2", "rustc-demangle", "rustc_ast", "rustc_attr", @@ -3657,7 +3669,7 @@ dependencies = [ "itertools 0.12.1", "jobserver", "libc", - "object", + "object 0.32.2", "pathdiff", "regex", "rustc_arena", @@ -4614,7 +4626,7 @@ name = "rustc_target" version = "0.0.0" dependencies = [ "bitflags 2.4.2", - "object", + "object 0.32.2", "rustc_abi", "rustc_data_structures", "rustc_feature", @@ -4881,6 +4893,17 @@ dependencies = [ "twox-hash", ] +[[package]] +name = "ruzstd" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5174a470eeb535a721ae9fdd6e291c2411a906b96592182d05217591d5c5cf7b" +dependencies = [ + "byteorder", + "derive_more", + "twox-hash", +] + [[package]] name = "ryu" version = "1.0.17" @@ -5184,7 +5207,7 @@ dependencies = [ "hermit-abi", "libc", "miniz_oxide", - "object", + "object 0.32.2", "panic_abort", "panic_unwind", "profiler_builtins", @@ -5501,7 +5524,7 @@ checksum = "4db52ee8fec06e119b692ef3dd2c4cf621a99204c1b8c47407870ed050305b9b" dependencies = [ "gimli", "hashbrown", - "object", + "object 0.32.2", "tracing", ] diff --git a/Cargo.toml b/Cargo.toml index 5dd315ef2f7..e12c968e205 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,15 +66,6 @@ exclude = [ ] [profile.release.package.compiler_builtins] -# The compiler-builtins crate cannot reference libcore, and its own CI will -# verify that this is the case. This requires, however, that the crate is built -# without overflow checks and debug assertions. Forcefully disable debug -# assertions and overflow checks here which should ensure that even if these -# assertions are enabled for libstd we won't enable them for compiler_builtins -# which should ensure we still link everything correctly. -debug-assertions = false -overflow-checks = false - # For compiler-builtins we always use a high number of codegen units. # The goal here is to place every single intrinsic into its own object # file to avoid symbol clashes with the system libgcc if possible. Note diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 09286a8ed24..8fd84d8c053 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1693,11 +1693,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.set_debug_loc(&mut bx, mir::SourceInfo::outermost(self.mir.span)); - let (fn_abi, fn_ptr, _instance) = common::build_langcall(&bx, None, reason.lang_item()); - let fn_ty = bx.fn_decl_backend_type(fn_abi); + let (fn_abi, fn_ptr, instance) = common::build_langcall(&bx, None, reason.lang_item()); + if is_call_from_compiler_builtins_to_upstream_monomorphization(bx.tcx(), instance) { + bx.abort(); + } else { + let fn_ty = bx.fn_decl_backend_type(fn_abi); - let llret = bx.call(fn_ty, None, Some(fn_abi), fn_ptr, &[], funclet.as_ref()); - bx.apply_attrs_to_cleanup_callsite(llret); + let llret = bx.call(fn_ty, None, Some(fn_abi), fn_ptr, &[], funclet.as_ref()); + bx.apply_attrs_to_cleanup_callsite(llret); + } bx.unreachable(); diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 814c3629b08..b82e959844f 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -10,6 +10,7 @@ use rustc_hir::lang_items::LangItem; use rustc_index::bit_set::FiniteBitSet; use rustc_macros::HashStable; use rustc_middle::ty::normalize_erasing_regions::NormalizationError; +use rustc_span::def_id::LOCAL_CRATE; use rustc_span::Symbol; use std::assert_matches::assert_matches; @@ -168,6 +169,11 @@ impl<'tcx> Instance<'tcx> { // If this a non-generic instance, it cannot be a shared monomorphization. self.args.non_erasable_generics(tcx, self.def_id()).next()?; + // compiler_builtins cannot use upstream monomorphizations. + if tcx.is_compiler_builtins(LOCAL_CRATE) { + return None; + } + match self.def { InstanceDef::Item(def) => tcx .upstream_monomorphizations_for(def) diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml index 958aef69572..d8bb8c643d1 100644 --- a/src/tools/run-make-support/Cargo.toml +++ b/src/tools/run-make-support/Cargo.toml @@ -4,4 +4,5 @@ version = "0.0.0" edition = "2021" [dependencies] +object = "0.34.0" wasmparser = "0.118.2" diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 674860f8413..e5e7b559c92 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -2,6 +2,7 @@ use std::env; use std::path::{Path, PathBuf}; use std::process::{Command, Output}; +pub use object; pub use wasmparser; pub fn out_dir() -> PathBuf { diff --git a/tests/run-make/compiler-builtins/rmake.rs b/tests/run-make/compiler-builtins/rmake.rs new file mode 100644 index 00000000000..e7a5e8addbe --- /dev/null +++ b/tests/run-make/compiler-builtins/rmake.rs @@ -0,0 +1,142 @@ +//! The compiler_builtins library is special. It can call functions in core, but it must not +//! require linkage against a build of core. If it ever does, building the standard library *may* +//! result in linker errors, depending on whether the linker in use applies optimizations first or +//! resolves symbols first. So the portable and safe approach is to forbid such a linkage +//! requirement entirely. +//! +//! In addition, whether compiler_builtins requires linkage against core can depend on optimization +//! settings. Turning off optimizations and enabling debug assertions tends to produce the most +//! dependence on core that is possible, so that is the configuration we test here. + +#![deny(warnings)] + +extern crate run_make_support; + +use run_make_support::object; +use run_make_support::object::read::archive::ArchiveFile; +use run_make_support::object::read::Object; +use run_make_support::object::ObjectSection; +use run_make_support::object::ObjectSymbol; +use run_make_support::object::RelocationTarget; +use run_make_support::out_dir; +use std::collections::HashSet; + +const MANIFEST: &str = r#" +[package] +name = "scratch" +version = "0.1.0" +edition = "2021" + +[lib] +path = "lib.rs""#; + +fn main() { + let target_dir = out_dir().join("target"); + let target = std::env::var("TARGET").unwrap(); + if target.starts_with("wasm") || target.starts_with("nvptx") { + // wasm and nvptx targets don't produce rlib files that object can parse. + return; + } + + println!("Testing compiler_builtins for {}", target); + + // Set up the tiniest Cargo project: An empty no_std library. Just enough to run -Zbuild-std. + let manifest_path = out_dir().join("Cargo.toml"); + std::fs::write(&manifest_path, MANIFEST.as_bytes()).unwrap(); + std::fs::write(out_dir().join("lib.rs"), b"#![no_std]").unwrap(); + + let path = std::env::var("PATH").unwrap(); + let rustc = std::env::var("RUSTC").unwrap(); + let bootstrap_cargo = std::env::var("BOOTSTRAP_CARGO").unwrap(); + let status = std::process::Command::new(bootstrap_cargo) + .args([ + "build", + "--manifest-path", + manifest_path.to_str().unwrap(), + "-Zbuild-std=core", + "--target", + &target, + ]) + .env_clear() + .env("PATH", path) + .env("RUSTC", rustc) + .env("RUSTFLAGS", "-Copt-level=0 -Cdebug-assertions=yes") + .env("CARGO_TARGET_DIR", &target_dir) + .env("RUSTC_BOOTSTRAP", "1") + .status() + .unwrap(); + + assert!(status.success()); + + let rlibs_path = target_dir.join(target).join("debug").join("deps"); + let compiler_builtins_rlib = std::fs::read_dir(rlibs_path) + .unwrap() + .find_map(|e| { + let path = e.unwrap().path(); + let file_name = path.file_name().unwrap().to_str().unwrap(); + if file_name.starts_with("libcompiler_builtins") && file_name.ends_with(".rlib") { + Some(path) + } else { + None + } + }) + .unwrap(); + + // rlib files are archives, where the archive members each a CGU, and we also have one called + // lib.rmeta which is the encoded metadata. Each of the CGUs is an object file. + let data = std::fs::read(compiler_builtins_rlib).unwrap(); + + let mut defined_symbols = HashSet::new(); + let mut undefined_relocations = HashSet::new(); + + let archive = ArchiveFile::parse(&*data).unwrap(); + for member in archive.members() { + let member = member.unwrap(); + if member.name() == b"lib.rmeta" { + continue; + } + let data = member.data(&*data).unwrap(); + let object = object::File::parse(&*data).unwrap(); + + // Record all defined symbols in this CGU. + for symbol in object.symbols() { + if !symbol.is_undefined() { + let name = symbol.name().unwrap(); + defined_symbols.insert(name); + } + } + + // Find any relocations against undefined symbols. Calls within this CGU are relocations + // against a defined symbol. + for (_offset, relocation) in object.sections().flat_map(|section| section.relocations()) { + let RelocationTarget::Symbol(symbol_index) = relocation.target() else { + continue; + }; + let symbol = object.symbol_by_index(symbol_index).unwrap(); + if symbol.is_undefined() { + let name = symbol.name().unwrap(); + undefined_relocations.insert(name); + } + } + } + + // We can have symbols in the compiler_builtins rlib that are actually from core, if they were + // monomorphized in the compiler_builtins crate. This is totally fine, because though the call + // is to a function in core, it's resolved internally. + // + // It is normal to have relocations against symbols not defined in the rlib for things like + // unwinding, or math functions provided the target's platform libraries. Finding these is not + // a problem, we want to specifically ban relocations against core which are not resolved + // internally. + undefined_relocations + .retain(|symbol| !defined_symbols.contains(symbol) && symbol.contains("core")); + + if !undefined_relocations.is_empty() { + panic!( + "compiler_builtins must not link against core, but it does. \n\ + These symbols may be undefined in a debug build of compiler_builtins:\n\ + {:?}", + undefined_relocations + ); + } +}