Skip to content

Commit 48494a0

Browse files
committed
Add rustc_panic_entrypoint
1 parent a33907a commit 48494a0

43 files changed

Lines changed: 282 additions & 169 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1246,6 +1246,15 @@ impl<S: Stage> NoArgsAttributeParser<S> for RustcIntrinsicConstStableIndirectPar
12461246
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcIntrinsicConstStableIndirect;
12471247
}
12481248

1249+
pub(crate) struct RustcPanicEntrypointParser;
1250+
1251+
impl<S: Stage> NoArgsAttributeParser<S> for RustcPanicEntrypointParser {
1252+
const PATH: &[Symbol] = &[sym::rustc_panic_entrypoint];
1253+
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
1254+
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
1255+
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcPanicEntrypoint;
1256+
}
1257+
12491258
pub(crate) struct RustcStrictCoherenceParser;
12501259

12511260
impl<S: Stage> NoArgsAttributeParser<S> for RustcStrictCoherenceParser {

‎compiler/rustc_attr_parsing/src/context.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ attribute_parsers!(
299299
Single<WithoutArgs<RustcObjectLifetimeDefaultParser>>,
300300
Single<WithoutArgs<RustcOffloadKernelParser>>,
301301
Single<WithoutArgs<RustcOutlivesParser>>,
302+
Single<WithoutArgs<RustcPanicEntrypointParser>>,
302303
Single<WithoutArgs<RustcPassIndirectlyInNonRusticAbisParser>>,
303304
Single<WithoutArgs<RustcPreserveUbChecksParser>>,
304305
Single<WithoutArgs<RustcProcMacroDeclsParser>>,

‎compiler/rustc_codegen_cranelift/src/base.rs‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use rustc_middle::ty::layout::FnAbiOf;
1414
use rustc_middle::ty::print::with_no_trimmed_paths;
1515
use rustc_session::config::OutputFilenames;
1616
use rustc_span::Symbol;
17+
use rustc_target::spec::PanicStrategy;
1718

1819
use crate::constant::ConstantCx;
1920
use crate::debuginfo::{FunctionDebugContext, TypeDebugContext};
@@ -384,6 +385,11 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
384385
fx.bcx.switch_to_block(failure);
385386
fx.bcx.ins().nop();
386387

388+
if fx.tcx.sess.panic_strategy() == PanicStrategy::ImmediateAbort {
389+
fx.bcx.ins().trap(TrapCode::user(1 /* unreachable */).unwrap());
390+
continue;
391+
}
392+
387393
match &**msg {
388394
AssertKind::BoundsCheck { len, index } => {
389395
let len = codegen_operand(fx, len).load_scalar(fx);
@@ -1052,6 +1058,10 @@ pub(crate) fn codegen_panic_nounwind<'tcx>(
10521058
msg_str: &str,
10531059
span: Span,
10541060
) {
1061+
if fx.tcx.sess.panic_strategy() == PanicStrategy::ImmediateAbort {
1062+
fx.bcx.ins().trap(TrapCode::user(1 /* unreachable */).unwrap());
1063+
}
1064+
10551065
let msg_ptr = crate::constant::pointer_for_anonymous_str(fx, msg_str);
10561066
let msg_len = fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(msg_str.len()).unwrap());
10571067
let args = [msg_ptr, msg_len];

‎compiler/rustc_codegen_ssa/src/codegen_attrs.rs‎

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use rustc_abi::{Align, ExternAbi};
44
use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode};
55
use rustc_ast::{LitKind, MetaItem, MetaItemInner};
66
use rustc_hir::attrs::{
7-
AttributeKind, EiiImplResolution, InlineAttr, Linkage, RtsanSetting, UsedBy,
7+
AttributeKind, EiiImplResolution, InlineAttr, Linkage, OptimizeAttr, RtsanSetting, UsedBy,
88
};
99
use rustc_hir::def::DefKind;
1010
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
@@ -19,7 +19,7 @@ use rustc_middle::ty::{self as ty, TyCtxt};
1919
use rustc_session::lint;
2020
use rustc_session::parse::feature_err;
2121
use rustc_span::{Span, sym};
22-
use rustc_target::spec::Os;
22+
use rustc_target::spec::{Os, PanicStrategy};
2323

2424
use crate::errors;
2525
use crate::target_features::{
@@ -391,6 +391,28 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code
391391
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
392392
}
393393
}
394+
395+
if tcx.is_panic_entrypoint(did) {
396+
// Panic entrypoints are always cold.
397+
//
398+
// If we have immediate-abort enabled, we want panic entrypoints to be inlined. We have a
399+
// MIR transform that tries to patch out immediate-abort panic entrypoint calls, but it can
400+
// miss indirect calls. In such cases, this should encourage the optimizer to clean up the
401+
// mess that we missed.
402+
//
403+
// When the panic strategies that support panic messages are enabled, we want panic
404+
// entrypoints outlined and optimized for size.
405+
// Most panic entrypoints want #[track_caller] but not all, so we do not add it.
406+
// FIXME: It seems unlikely that all choices of #[track_caller] were deliberate, but it can
407+
// have a code size impact, so it makes sense to apply judiciously.
408+
codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD;
409+
if tcx.sess.panic_strategy() == PanicStrategy::ImmediateAbort {
410+
codegen_fn_attrs.inline = InlineAttr::Always;
411+
} else {
412+
codegen_fn_attrs.inline = InlineAttr::Never;
413+
codegen_fn_attrs.optimize = OptimizeAttr::Size;
414+
}
415+
}
394416
}
395417

396418
fn check_result(

‎compiler/rustc_codegen_ssa/src/mir/block.rs‎

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use rustc_session::config::OptLevel;
1515
use rustc_span::Span;
1616
use rustc_span::source_map::Spanned;
1717
use rustc_target::callconv::{ArgAbi, ArgAttributes, CastTarget, FnAbi, PassMode};
18+
use rustc_target::spec::PanicStrategy;
1819
use tracing::{debug, info};
1920

2021
use super::operand::OperandRef;
@@ -176,22 +177,22 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
176177
if let Some(instance) = instance
177178
&& is_call_from_compiler_builtins_to_upstream_monomorphization(tcx, instance)
178179
{
179-
if destination.is_some() {
180+
if tcx.is_panic_entrypoint(instance.def_id()) {
181+
info!(
182+
"compiler_builtins call to panic entrypoint {:?} replaced with abort",
183+
instance.def_id()
184+
);
185+
bx.abort();
186+
bx.unreachable();
187+
return MergingSucc::False;
188+
} else {
180189
let caller_def = fx.instance.def_id();
181190
let e = CompilerBuiltinsCannotCall {
182191
span: tcx.def_span(caller_def),
183192
caller: with_no_trimmed_paths!(tcx.def_path_str(caller_def)),
184193
callee: with_no_trimmed_paths!(tcx.def_path_str(instance.def_id())),
185194
};
186195
tcx.dcx().emit_err(e);
187-
} else {
188-
info!(
189-
"compiler_builtins call to diverging function {:?} replaced with abort",
190-
instance.def_id()
191-
);
192-
bx.abort();
193-
bx.unreachable();
194-
return MergingSucc::False;
195196
}
196197
}
197198

@@ -737,6 +738,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
737738
bx.switch_to_block(panic_block);
738739
self.set_debug_loc(bx, terminator.source_info);
739740

741+
if bx.tcx().sess.panic_strategy() == PanicStrategy::ImmediateAbort {
742+
bx.abort();
743+
bx.unreachable();
744+
return MergingSucc::False;
745+
}
746+
740747
// Get the location information.
741748
let location = self.get_caller_location(bx, terminator.source_info).immediate();
742749

‎compiler/rustc_feature/src/builtin_attrs.rs‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1321,6 +1321,11 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
13211321
"`#[rustc_non_const_trait_method]` should only used by the standard library to mark trait methods \
13221322
as non-const to allow large traits an easier transition to const"
13231323
),
1324+
rustc_attr!(
1325+
rustc_panic_entrypoint, AttributeType::Normal, template!(Word),
1326+
WarnFollowing, EncodeCrossCrate::Yes,
1327+
"`#[rustc_panic_entrypoint]` makes this function patchable by panic=immediate-abort",
1328+
),
13241329

13251330
BuiltinAttribute {
13261331
name: sym::rustc_diagnostic_item,

‎compiler/rustc_hir/src/attrs/data_structures.rs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,6 +1283,9 @@ pub enum AttributeKind {
12831283
/// Represents `#[rustc_outlives]`
12841284
RustcOutlives,
12851285

1286+
/// Represents `#[rustc_panic_entrypoint]`
1287+
RustcPanicEntrypoint,
1288+
12861289
/// Represents `#[rustc_paren_sugar]`.
12871290
RustcParenSugar(Span),
12881291

‎compiler/rustc_hir/src/attrs/encode_cross_crate.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ impl AttributeKind {
154154
RustcObjectLifetimeDefault => No,
155155
RustcOffloadKernel => Yes,
156156
RustcOutlives => No,
157+
RustcPanicEntrypoint => Yes,
157158
RustcParenSugar(..) => No,
158159
RustcPassByValue(..) => Yes,
159160
RustcPassIndirectlyInNonRusticAbis(..) => No,

‎compiler/rustc_hir/src/lang_items.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ language_item_table! {
235235
FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
236236
FnOnce, sym::fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1);
237237

238+
AbortIntrinsic, sym::abort_intrinsic, abort_intrinsic, Target::Fn, GenericRequirement::Exact(0);
238239
AsyncFn, sym::async_fn, async_fn_trait, Target::Trait, GenericRequirement::Exact(1);
239240
AsyncFnMut, sym::async_fn_mut, async_fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
240241
AsyncFnOnce, sym::async_fn_once, async_fn_once_trait, Target::Trait, GenericRequirement::Exact(1);

‎compiler/rustc_lint/src/builtin.rs‎

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use rustc_attr_parsing::AttributeParser;
2525
use rustc_errors::{Applicability, LintDiagnostic, msg};
2626
use rustc_feature::GateIssue;
2727
use rustc_hir as hir;
28-
use rustc_hir::attrs::{AttributeKind, DocAttribute};
28+
use rustc_hir::attrs::{AttributeKind, DocAttribute, InlineAttr};
2929
use rustc_hir::def::{DefKind, Res};
3030
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
3131
use rustc_hir::intravisit::FnKind as HirFnKind;
@@ -3188,3 +3188,65 @@ impl EarlyLintPass for SpecialModuleName {
31883188
}
31893189
}
31903190
}
3191+
3192+
declare_lint! {
3193+
/// The `missing_panic_entrypoint` lint detects forgotten use of #[rustc_panic_entrypoint].
3194+
///
3195+
/// This lint is intended to ensure that panic=immediate-abort can function as designed,
3196+
/// because it uses #[rustc_panic_entrypoint] to locate functions that should be outlined
3197+
/// for other panic modes, and be deleted entirely when immediate-abort is enabled.
3198+
pub MISSING_PANIC_ENTRYPOINT,
3199+
Allow,
3200+
"detects missing #[rustc_panic_entrypoint]"
3201+
}
3202+
3203+
declare_lint_pass!(MissingPanicEntrypoint => [MISSING_PANIC_ENTRYPOINT]);
3204+
3205+
fn has_panic_entrypoint(attrs: &[hir::Attribute]) -> bool {
3206+
attrs
3207+
.iter()
3208+
.any(|attr| matches!(attr, hir::Attribute::Parsed(AttributeKind::RustcPanicEntrypoint)))
3209+
}
3210+
3211+
fn has_inline_encouragement(attrs: &[hir::Attribute]) -> bool {
3212+
attrs.iter().any(|attr| {
3213+
matches!(
3214+
attr,
3215+
hir::Attribute::Parsed(hir::attrs::AttributeKind::Inline(
3216+
InlineAttr::Hint | InlineAttr::Always | InlineAttr::Force { .. },
3217+
_
3218+
))
3219+
)
3220+
})
3221+
}
3222+
3223+
fn has_rustc_intrinsic(attrs: &[hir::Attribute]) -> bool {
3224+
attrs.iter().any(|attr| matches!(attr, hir::Attribute::Parsed(AttributeKind::RustcIntrinsic)))
3225+
}
3226+
3227+
impl<'tcx> LateLintPass<'tcx> for MissingPanicEntrypoint {
3228+
fn check_fn(
3229+
&mut self,
3230+
cx: &LateContext<'tcx>,
3231+
_: HirFnKind<'tcx>,
3232+
fn_decl: &'tcx FnDecl<'tcx>,
3233+
_: &'tcx Body<'tcx>,
3234+
span: Span,
3235+
def_id: LocalDefId,
3236+
) {
3237+
if matches!(fn_decl.output, hir::FnRetTy::Return(hir::Ty { kind: hir::TyKind::Never, .. }))
3238+
{
3239+
let attrs = cx.tcx.hir_attrs(cx.tcx.local_def_id_to_hir_id(def_id));
3240+
if has_rustc_intrinsic(attrs) {
3241+
return;
3242+
}
3243+
if !has_inline_encouragement(attrs) && !has_panic_entrypoint(attrs) {
3244+
cx.emit_span_lint(
3245+
MISSING_PANIC_ENTRYPOINT,
3246+
span,
3247+
crate::lints::MissingPanicEntrypoint,
3248+
);
3249+
}
3250+
}
3251+
}
3252+
}

0 commit comments

Comments
 (0)