Skip to content

Commit

Permalink
Codegen const panic messages as function calls
Browse files Browse the repository at this point in the history
This skips emitting extra arguments at every callsite (of which there
can be many). For a librustc_driver build with overflow checks enabled,
this cuts 0.7MB from the resulting binary.
  • Loading branch information
Mark-Simulacrum committed Mar 22, 2024
1 parent 0ad927c commit 00f4daa
Show file tree
Hide file tree
Showing 12 changed files with 255 additions and 63 deletions.
30 changes: 30 additions & 0 deletions compiler/rustc_codegen_cranelift/example/mini_core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,36 @@ pub fn panic(_msg: &'static str) -> ! {
}
}

macro_rules! panic_const {
($($lang:ident = $message:expr,)+) => {
#[cfg(not(bootstrap))]
pub mod panic_const {
use super::*;

$(
#[track_caller]
#[lang = stringify!($lang)]
pub fn $lang() -> ! {
panic($message);
}
)+
}
}
}

panic_const! {
panic_const_add_overflow = "attempt to add with overflow",
panic_const_sub_overflow = "attempt to subtract with overflow",
panic_const_mul_overflow = "attempt to multiply with overflow",
panic_const_div_overflow = "attempt to divide with overflow",
panic_const_rem_overflow = "attempt to calculate the remainder with overflow",
panic_const_neg_overflow = "attempt to negate with overflow",
panic_const_shr_overflow = "attempt to shift right with overflow",
panic_const_shl_overflow = "attempt to shift left with overflow",
panic_const_div_by_zero = "attempt to divide by zero",
panic_const_rem_by_zero = "attempt to calculate the remainder with a divisor of zero",
}

#[lang = "panic_bounds_check"]
#[track_caller]
fn panic_bounds_check(index: usize, len: usize) -> ! {
Expand Down
24 changes: 8 additions & 16 deletions compiler/rustc_codegen_cranelift/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,8 +369,14 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
);
}
_ => {
let msg_str = msg.description();
codegen_panic(fx, msg_str, source_info);
let location = fx.get_caller_location(source_info).load_scalar(fx);

codegen_panic_inner(
fx,
msg.panic_function(),
&[location],
Some(source_info.span),
);
}
}
}
Expand Down Expand Up @@ -954,20 +960,6 @@ pub(crate) fn codegen_operand<'tcx>(
}
}

pub(crate) fn codegen_panic<'tcx>(
fx: &mut FunctionCx<'_, '_, 'tcx>,
msg_str: &str,
source_info: mir::SourceInfo,
) {
let location = fx.get_caller_location(source_info).load_scalar(fx);

let msg_ptr = fx.anonymous_str(msg_str);
let msg_len = fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(msg_str.len()).unwrap());
let args = [msg_ptr, msg_len, location];

codegen_panic_inner(fx, rustc_hir::LangItem::Panic, &args, Some(source_info.span));
}

pub(crate) fn codegen_panic_nounwind<'tcx>(
fx: &mut FunctionCx<'_, '_, 'tcx>,
msg_str: &str,
Expand Down
30 changes: 30 additions & 0 deletions compiler/rustc_codegen_gcc/example/mini_core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,36 @@ pub fn panic(_msg: &'static str) -> ! {
}
}

macro_rules! panic_const {
($($lang:ident = $message:expr,)+) => {
#[cfg(not(bootstrap))]
pub mod panic_const {
use super::*;

$(
#[track_caller]
#[lang = stringify!($lang)]
pub fn $lang() -> ! {
panic($message);
}
)+
}
}
}

panic_const! {
panic_const_add_overflow = "attempt to add with overflow",
panic_const_sub_overflow = "attempt to subtract with overflow",
panic_const_mul_overflow = "attempt to multiply with overflow",
panic_const_div_overflow = "attempt to divide with overflow",
panic_const_rem_overflow = "attempt to calculate the remainder with overflow",
panic_const_neg_overflow = "attempt to negate with overflow",
panic_const_shr_overflow = "attempt to shift right with overflow",
panic_const_shl_overflow = "attempt to shift left with overflow",
panic_const_div_by_zero = "attempt to divide by zero",
panic_const_rem_by_zero = "attempt to calculate the remainder with a divisor of zero",
}

#[lang = "panic_cannot_unwind"]
fn panic_cannot_unwind() -> ! {
unsafe {
Expand Down
6 changes: 2 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,8 @@ 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.
(msg.panic_function(), vec![location])
}
};

Expand Down
19 changes: 19 additions & 0 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,25 @@ language_item_table! {
PanicImpl, sym::panic_impl, panic_impl, Target::Fn, GenericRequirement::None;
PanicCannotUnwind, sym::panic_cannot_unwind, panic_cannot_unwind, Target::Fn, GenericRequirement::Exact(0);
PanicInCleanup, sym::panic_in_cleanup, panic_in_cleanup, Target::Fn, GenericRequirement::Exact(0);
/// Constant panic messages, used for codegen of MIR asserts.
PanicAddOverflow, sym::panic_const_add_overflow, panic_const_add_overflow, Target::Fn, GenericRequirement::None;
PanicSubOverflow, sym::panic_const_sub_overflow, panic_const_sub_overflow, Target::Fn, GenericRequirement::None;
PanicMulOverflow, sym::panic_const_mul_overflow, panic_const_mul_overflow, Target::Fn, GenericRequirement::None;
PanicDivOverflow, sym::panic_const_div_overflow, panic_const_div_overflow, Target::Fn, GenericRequirement::None;
PanicRemOverflow, sym::panic_const_rem_overflow, panic_const_rem_overflow, Target::Fn, GenericRequirement::None;
PanicNegOverflow, sym::panic_const_neg_overflow, panic_const_neg_overflow, Target::Fn, GenericRequirement::None;
PanicShrOverflow, sym::panic_const_shr_overflow, panic_const_shr_overflow, Target::Fn, GenericRequirement::None;
PanicShlOverflow, sym::panic_const_shl_overflow, panic_const_shl_overflow, Target::Fn, GenericRequirement::None;
PanicDivZero, sym::panic_const_div_by_zero, panic_const_div_by_zero, Target::Fn, GenericRequirement::None;
PanicRemZero, sym::panic_const_rem_by_zero, panic_const_rem_by_zero, Target::Fn, GenericRequirement::None;
PanicCoroutineResumed, sym::panic_const_coroutine_resumed, panic_const_coroutine_resumed, Target::Fn, GenericRequirement::None;
PanicAsyncFnResumed, sym::panic_const_async_fn_resumed, panic_const_async_fn_resumed, Target::Fn, GenericRequirement::None;
PanicAsyncGenFnResumed, sym::panic_const_async_gen_fn_resumed, panic_const_async_gen_fn_resumed, Target::Fn, GenericRequirement::None;
PanicGenFnNone, sym::panic_const_gen_fn_none, panic_const_gen_fn_none, Target::Fn, GenericRequirement::None;
PanicCoroutineResumedPanic, sym::panic_const_coroutine_resumed_panic, panic_const_coroutine_resumed_panic, Target::Fn, GenericRequirement::None;
PanicAsyncFnResumedPanic, sym::panic_const_async_fn_resumed_panic, panic_const_async_fn_resumed_panic, Target::Fn, GenericRequirement::None;
PanicAsyncGenFnResumedPanic, sym::panic_const_async_gen_fn_resumed_panic, panic_const_async_gen_fn_resumed_panic, Target::Fn, GenericRequirement::None;
PanicGenFnNonePanic, sym::panic_const_gen_fn_none_panic, panic_const_gen_fn_none_panic, Target::Fn, GenericRequirement::None;
/// libstd panic entry point. Necessary for const eval to be able to catch it
BeginPanic, sym::begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None;

Expand Down
73 changes: 49 additions & 24 deletions compiler/rustc_middle/src/mir/terminator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,44 +149,45 @@ impl<O> AssertKind<O> {
matches!(self, OverflowNeg(..) | Overflow(Add | Sub | Mul | Shl | Shr, ..))
}

/// Get the message that is printed at runtime when this assertion fails.
/// Get the lang item that is invoked to print a static message when this assert fires.
///
/// The caller is expected to handle `BoundsCheck` and `MisalignedPointerDereference` by
/// invoking the appropriate lang item (panic_bounds_check/panic_misaligned_pointer_dereference)
/// instead of printing a static message.
pub fn description(&self) -> &'static str {
/// instead of printing a static message. Those have dynamic arguments that aren't present for
/// the rest of the messages here.
pub fn panic_function(&self) -> LangItem {
use AssertKind::*;
match self {
Overflow(BinOp::Add, _, _) => "attempt to add with overflow",
Overflow(BinOp::Sub, _, _) => "attempt to subtract with overflow",
Overflow(BinOp::Mul, _, _) => "attempt to multiply with overflow",
Overflow(BinOp::Div, _, _) => "attempt to divide with overflow",
Overflow(BinOp::Rem, _, _) => "attempt to calculate the remainder with overflow",
OverflowNeg(_) => "attempt to negate with overflow",
Overflow(BinOp::Shr, _, _) => "attempt to shift right with overflow",
Overflow(BinOp::Shl, _, _) => "attempt to shift left with overflow",
Overflow(BinOp::Add, _, _) => LangItem::PanicAddOverflow,
Overflow(BinOp::Sub, _, _) => LangItem::PanicSubOverflow,
Overflow(BinOp::Mul, _, _) => LangItem::PanicMulOverflow,
Overflow(BinOp::Div, _, _) => LangItem::PanicDivOverflow,
Overflow(BinOp::Rem, _, _) => LangItem::PanicRemOverflow,
OverflowNeg(_) => LangItem::PanicNegOverflow,
Overflow(BinOp::Shr, _, _) => LangItem::PanicShrOverflow,
Overflow(BinOp::Shl, _, _) => LangItem::PanicShlOverflow,
Overflow(op, _, _) => bug!("{:?} cannot overflow", op),
DivisionByZero(_) => "attempt to divide by zero",
RemainderByZero(_) => "attempt to calculate the remainder with a divisor of zero",
ResumedAfterReturn(CoroutineKind::Coroutine(_)) => "coroutine resumed after completion",
DivisionByZero(_) => LangItem::PanicDivZero,
RemainderByZero(_) => LangItem::PanicRemZero,
ResumedAfterReturn(CoroutineKind::Coroutine(_)) => LangItem::PanicCoroutineResumed,
ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
"`async fn` resumed after completion"
LangItem::PanicAsyncFnResumed
}
ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
"`async gen fn` resumed after completion"
LangItem::PanicAsyncGenFnResumed
}
ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
"`gen fn` should just keep returning `None` after completion"
LangItem::PanicGenFnNone
}
ResumedAfterPanic(CoroutineKind::Coroutine(_)) => "coroutine resumed after panicking",
ResumedAfterPanic(CoroutineKind::Coroutine(_)) => LangItem::PanicCoroutineResumedPanic,
ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
"`async fn` resumed after panicking"
LangItem::PanicAsyncFnResumedPanic
}
ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
"`async gen fn` resumed after panicking"
LangItem::PanicAsyncGenFnResumedPanic
}
ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
"`gen fn` should just keep returning `None` after panicking"
LangItem::PanicGenFnNonePanic
}

BoundsCheck { .. } | MisalignedPointerDereference { .. } => {
Expand All @@ -198,7 +199,7 @@ impl<O> AssertKind<O> {
/// Format the message arguments for the `assert(cond, msg..)` terminator in MIR printing.
///
/// Needs to be kept in sync with the run-time behavior (which is defined by
/// `AssertKind::description` and the lang items mentioned in its docs).
/// `AssertKind::panic_function` and the lang items mentioned in its docs).
/// Note that we deliberately show more details here than we do at runtime, such as the actual
/// numbers that overflowed -- it is much easier to do so here than at runtime.
pub fn fmt_assert_args<W: fmt::Write>(&self, f: &mut W) -> fmt::Result
Expand Down Expand Up @@ -246,20 +247,44 @@ impl<O> AssertKind<O> {
Overflow(BinOp::Shl, _, r) => {
write!(f, "\"attempt to shift left by `{{}}`, which would overflow\", {r:?}")
}
Overflow(op, _, _) => bug!("{:?} cannot overflow", op),
MisalignedPointerDereference { required, found } => {
write!(
f,
"\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {required:?}, {found:?}"
)
}
_ => write!(f, "\"{}\"", self.description()),
ResumedAfterReturn(CoroutineKind::Coroutine(_)) => {
write!(f, "\"coroutine resumed after completion\"")
}
ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
write!(f, "\"`async fn` resumed after completion\"")
}
ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
write!(f, "\"`async gen fn` resumed after completion\"")
}
ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
write!(f, "\"`gen fn` should just keep returning `None` after completion\"")
}
ResumedAfterPanic(CoroutineKind::Coroutine(_)) => {
write!(f, "\"coroutine resumed after panicking\"")
}
ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
write!(f, "\"`async fn` resumed after panicking\"")
}
ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
write!(f, "\"`async gen fn` resumed after panicking\"")
}
ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
write!(f, "\"`gen fn` should just keep returning `None` after panicking\"")
}
}
}

/// Format the diagnostic message for use in a lint (e.g. when the assertion fails during const-eval).
///
/// Needs to be kept in sync with the run-time behavior (which is defined by
/// `AssertKind::description` and the lang items mentioned in its docs).
/// `AssertKind::panic_function` and the lang items mentioned in its docs).
/// Note that we deliberately show more details here than we do at runtime, such as the actual
/// numbers that overflowed -- it is much easier to do so here than at runtime.
pub fn diagnostic_message(&self) -> DiagMessage {
Expand Down
21 changes: 11 additions & 10 deletions compiler/rustc_monomorphize/src/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -971,16 +971,17 @@ 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
}
_ => LangItem::Panic,
};
push_mono_lang_item(self, lang_item);
}
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);
}
_ => {
push_mono_lang_item(self, msg.panic_function());
}
},
mir::TerminatorKind::UnwindTerminate(reason) => {
push_mono_lang_item(self, reason.lang_item());
}
Expand Down
18 changes: 18 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1297,6 +1297,24 @@ symbols! {
panic_abort,
panic_bounds_check,
panic_cannot_unwind,
panic_const_add_overflow,
panic_const_async_fn_resumed,
panic_const_async_fn_resumed_panic,
panic_const_async_gen_fn_resumed,
panic_const_async_gen_fn_resumed_panic,
panic_const_coroutine_resumed,
panic_const_coroutine_resumed_panic,
panic_const_div_by_zero,
panic_const_div_overflow,
panic_const_gen_fn_none,
panic_const_gen_fn_none_panic,
panic_const_mul_overflow,
panic_const_neg_overflow,
panic_const_rem_by_zero,
panic_const_rem_overflow,
panic_const_shl_overflow,
panic_const_shr_overflow,
panic_const_sub_overflow,
panic_fmt,
panic_handler,
panic_impl,
Expand Down
Loading

0 comments on commit 00f4daa

Please sign in to comment.