Auto merge of #122580 - saethlin:compiler-builtins-can-panic, r=pnkfelix
"Handle" calls to upstream monomorphizations in compiler_builtins This is pretty cooked, but I think it works. compiler-builtins has a long-standing problem that at link time, its rlib cannot contain any calls to `core`. And yet, in codegen we _love_ inserting calls to symbols in `core`, generally from various panic entrypoints. I intend this PR to attack that problem as completely as possible. When we generate a function call, we now check if we are generating a function call from `compiler_builtins` and whether the callee is a function which was not lowered in the current crate, meaning we will have to link to it. If those conditions are met, actually generating the call is asking for a linker error. So we don't. If the callee diverges, we lower to an abort with the same behavior as `core::intrinsics::abort`. If the callee does not diverge, we produce an error. This means that compiler-builtins can contain panics, but they'll SIGILL instead of panicking. I made non-diverging calls a compile error because I'm guessing that they'd mostly get into compiler-builtins by someone making a mistake while working on the crate, and compile errors are better than linker errors. We could turn such calls into aborts as well if that's preferred.
This commit is contained in:
commit
b3df0d7e5e
17 changed files with 284 additions and 29 deletions
|
@ -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" }
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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<Span>,
|
||||
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
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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, <Bx as BackendTypes>::Value>],
|
||||
instance: Option<Instance<'tcx>>,
|
||||
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,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) = 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();
|
||||
|
||||
|
|
|
@ -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`.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue