From 12078d552e50b2817e66191b60a650b13310d88a Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sun, 17 Mar 2024 22:26:39 -0400 Subject: [PATCH] Codegen const panic messages as function calls This skips emitting extra arguments at every callsite (of which there can be many). --- compiler/rustc_codegen_ssa/src/mir/block.rs | 24 +++++++++++++++--- compiler/rustc_hir/src/lang_items.rs | 1 + compiler/rustc_monomorphize/src/collector.rs | 26 +++++++++++++------- compiler/rustc_span/src/symbol.rs | 1 + library/core/src/panicking.rs | 22 +++++++++++++++++ 5 files changed, 61 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 9bb2a52826585..29b7c6f8c645c 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -651,10 +651,26 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { (LangItem::PanicMisalignedPointerDereference, vec![required, found, location]) } _ => { - let msg = bx.const_str(msg.description()); - // It's `pub fn panic(expr: &str)`, with the wide reference being passed - // as two arguments, and `#[track_caller]` adds an implicit third argument. - (LangItem::Panic, vec![msg.0, msg.1, location]) + // It's `pub fn panic()` and `#[track_caller]` adds an implicit argument. + // + // Effects add an implicit `const host: bool` so we add that in as well, matching + // behavior from `ty::Instance::mono`. + + let tcx = bx.tcx(); + let def_id = tcx.require_lang_item(LangItem::PanicConst, Some(span)); + + let val_tree = ty::ValTree::from_raw_bytes(tcx, msg.description().as_bytes()); + let desc = ty::Const::new_value(tcx, val_tree, Ty::new_static_str(tcx)); + let args = tcx.mk_args(&[ty::GenericArg::from(desc), tcx.consts.true_.into()]); + let instance = ty::Instance::new(def_id, args); + let (fn_abi, llfn) = + (bx.fn_abi_of_instance(instance, ty::List::empty()), bx.get_fn_addr(instance)); + + // Codegen the actual panic invoke/call. + let merging_succ = + helper.do_call(self, bx, fn_abi, llfn, &[location], None, unwind, &[], false); + assert_eq!(merging_succ, MergingSucc::False); + return MergingSucc::False; } }; diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 5118bf5c3b7ab..5b70d6663062d 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -236,6 +236,7 @@ language_item_table! { // is required to define it somewhere. Additionally, there are restrictions on crates that use // a weak lang item, but do not have it defined. Panic, sym::panic, panic_fn, Target::Fn, GenericRequirement::Exact(0); + PanicConst, sym::panic_const, panic_const_fn, Target::Fn, GenericRequirement::Exact(1); PanicNounwind, sym::panic_nounwind, panic_nounwind, Target::Fn, GenericRequirement::Exact(0); PanicFmt, sym::panic_fmt, panic_fmt, Target::Fn, GenericRequirement::None; ConstPanicFmt, sym::const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None; diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index cd9eb4916ce5d..958a47934c408 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -886,16 +886,24 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { } } } - mir::TerminatorKind::Assert { ref msg, .. } => { - let lang_item = match &**msg { - mir::AssertKind::BoundsCheck { .. } => LangItem::PanicBoundsCheck, - mir::AssertKind::MisalignedPointerDereference { .. } => { - LangItem::PanicMisalignedPointerDereference + mir::TerminatorKind::Assert { ref msg, .. } => match &**msg { + mir::AssertKind::BoundsCheck { .. } => { + push_mono_lang_item(self, LangItem::PanicBoundsCheck); + } + mir::AssertKind::MisalignedPointerDereference { .. } => { + push_mono_lang_item(self, LangItem::PanicMisalignedPointerDereference); + } + _ => { + let def_id = tcx.require_lang_item(LangItem::PanicConst, Some(source)); + let val_tree = ty::ValTree::from_raw_bytes(tcx, msg.description().as_bytes()); + let desc = ty::Const::new_value(tcx, val_tree, Ty::new_static_str(tcx)); + let args = tcx.mk_args(&[ty::GenericArg::from(desc), tcx.consts.true_.into()]); + let instance = ty::Instance::new(def_id, args); + if should_codegen_locally(tcx, &instance) { + self.output.push(create_fn_mono_item(tcx, instance, source)); } - _ => LangItem::Panic, - }; - push_mono_lang_item(self, lang_item); - } + } + }, mir::TerminatorKind::UnwindTerminate(reason) => { push_mono_lang_item(self, reason.lang_item()); } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e43c9533382e1..8df02e999c4a4 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1297,6 +1297,7 @@ symbols! { panic_abort, panic_bounds_check, panic_cannot_unwind, + panic_const, panic_fmt, panic_handler, panic_impl, diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 9e1184c8f5b88..f0061c9e111cb 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -145,6 +145,28 @@ pub const fn panic(expr: &'static str) -> ! { panic_fmt(fmt::Arguments::new_const(&[expr])); } +/// This is the panic called with compile-time messages. This eliminates everything except +/// the track-caller argument at call sites, while not requiring writing a bunch of code inside MIR +/// etc. for generating stub functions. +// +// never inline unless panic_immediate_abort to avoid code +// bloat at the call sites as much as possible +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] +#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[track_caller] +#[rustc_const_unstable(feature = "panic_internals", issue = "none")] +#[lang = "panic_const"] // needed by codegen for panic on overflow and other `Assert` MIR terminators +#[cfg(not(bootstrap))] +pub const fn panic_const() -> ! { + // Use Arguments::new_v1 instead of format_args!("{expr}") to potentially + // reduce size overhead. The format_args! macro uses str's Display trait to + // write expr, which calls Formatter::pad, which must accommodate string + // truncation and padding (even though none is used here). Using + // Arguments::new_v1 may allow the compiler to omit Formatter::pad from the + // output binary, saving up to a few kilobytes. + panic_fmt(fmt::Arguments::new_const(&[MESSAGE])); +} + /// Like `panic`, but without unwinding and track_caller to reduce the impact on codesize on the caller. /// If you want `#[track_caller]` for nicer errors, call `panic_nounwind_fmt` directly. #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]