Skip to content

Commit

Permalink
Auto merge of rust-lang#122671 - Mark-Simulacrum:const-panic-msg, r=<…
Browse files Browse the repository at this point in the history
…try>

Codegen const panic messages as function calls

This skips emitting extra arguments at every callsite (of which there can be many). We could handwrite these functions if the approach here proves fairly expensive at compile-time.

r? `@Mark-Simulacrum` (for perf, for now)
  • Loading branch information
bors committed Mar 18, 2024
2 parents 62f98b4 + 12078d5 commit 02fafb5
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 13 deletions.
24 changes: 20 additions & 4 deletions compiler/rustc_codegen_ssa/src/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<const M: &str>()` 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;
}
};

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
26 changes: 17 additions & 9 deletions compiler/rustc_monomorphize/src/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1297,6 +1297,7 @@ symbols! {
panic_abort,
panic_bounds_check,
panic_cannot_unwind,
panic_const,
panic_fmt,
panic_handler,
panic_impl,
Expand Down
22 changes: 22 additions & 0 deletions library/core/src/panicking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<const MESSAGE: &'static str>() -> ! {
// 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)]
Expand Down

0 comments on commit 02fafb5

Please sign in to comment.