Skip to content

Commit a700f93

Browse files
committed
Add rustc_panic_entrypoint
1 parent c7b206b commit a700f93

43 files changed

Lines changed: 286 additions & 167 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
@@ -1256,6 +1256,15 @@ impl<S: Stage> SingleAttributeParser<S> for RustcDefPathParser {
12561256
}
12571257
}
12581258

1259+
pub(crate) struct RustcPanicEntrypointParser;
1260+
1261+
impl<S: Stage> NoArgsAttributeParser<S> for RustcPanicEntrypointParser {
1262+
const PATH: &[Symbol] = &[sym::rustc_panic_entrypoint];
1263+
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
1264+
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
1265+
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcPanicEntrypoint;
1266+
}
1267+
12591268
pub(crate) struct RustcStrictCoherenceParser;
12601269

12611270
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
@@ -311,6 +311,7 @@ attribute_parsers!(
311311
Single<WithoutArgs<RustcNonnullOptimizationGuaranteedParser>>,
312312
Single<WithoutArgs<RustcNounwindParser>>,
313313
Single<WithoutArgs<RustcOffloadKernelParser>>,
314+
Single<WithoutArgs<RustcPanicEntrypointParser>>,
314315
Single<WithoutArgs<RustcParenSugarParser>>,
315316
Single<WithoutArgs<RustcPassByValueParser>>,
316317
Single<WithoutArgs<RustcPassIndirectlyInNonRusticAbisParser>>,

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, HasTypingEnv as _};
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);
@@ -1058,6 +1064,10 @@ pub(crate) fn codegen_panic_nounwind<'tcx>(
10581064
msg_str: &str,
10591065
span: Span,
10601066
) {
1067+
if fx.tcx.sess.panic_strategy() == PanicStrategy::ImmediateAbort {
1068+
fx.bcx.ins().trap(TrapCode::user(1 /* unreachable */).unwrap());
1069+
}
1070+
10611071
let msg_ptr = crate::constant::pointer_for_anonymous_str(fx, msg_str);
10621072
let msg_len = fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(msg_str.len()).unwrap());
10631073
let args = [msg_ptr, msg_len];

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use rustc_abi::{Align, ExternAbi};
22
use rustc_hir::attrs::{
3-
AttributeKind, EiiImplResolution, InlineAttr, Linkage, RtsanSetting, UsedBy,
3+
AttributeKind, EiiImplResolution, InlineAttr, Linkage, OptimizeAttr, RtsanSetting, UsedBy,
44
};
55
use rustc_hir::def::DefKind;
66
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
@@ -15,7 +15,7 @@ use rustc_middle::ty::{self as ty, TyCtxt};
1515
use rustc_session::lint;
1616
use rustc_session::parse::feature_err;
1717
use rustc_span::{Span, sym};
18-
use rustc_target::spec::Os;
18+
use rustc_target::spec::{Os, PanicStrategy};
1919

2020
use crate::errors;
2121
use crate::target_features::{
@@ -292,6 +292,9 @@ fn process_builtin_attrs(
292292
codegen_fn_attrs.patchable_function_entry =
293293
Some(PatchableFunctionEntry::from_prefix_and_entry(*prefix, *entry));
294294
}
295+
AttributeKind::RustcPanicEntrypoint => {
296+
codegen_fn_attrs.flags |= CodegenFnAttrFlags::PANIC_ENTRYPOINT
297+
}
295298
_ => {}
296299
}
297300
}
@@ -384,6 +387,28 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code
384387
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
385388
}
386389
}
390+
391+
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::PANIC_ENTRYPOINT) {
392+
// Panic entrypoints are always cold.
393+
//
394+
// If we have immediate-abort enabled, we want panic entrypoints to be inlined. We have a
395+
// MIR transform that tries to patch out immediate-abort panic entrypoint calls, but it can
396+
// miss indirect calls. In such cases, this should encourage the optimizer to clean up the
397+
// mess that we missed.
398+
//
399+
// When the panic strategies that support panic messages are enabled, we want panic
400+
// entrypoints outlined and optimized for size.
401+
// Most panic entrypoints want #[track_caller] but not all, so we do not add it.
402+
// FIXME: It seems unlikely that all choices of #[track_caller] were deliberate, but it can
403+
// have a code size impact, so it makes sense to apply judiciously.
404+
codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD;
405+
if tcx.sess.panic_strategy() == PanicStrategy::ImmediateAbort {
406+
codegen_fn_attrs.inline = InlineAttr::Always;
407+
} else {
408+
codegen_fn_attrs.inline = InlineAttr::Never;
409+
codegen_fn_attrs.optimize = OptimizeAttr::Size;
410+
}
411+
}
387412
}
388413

389414
#[derive(Diagnostic)]

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 instance.def.is_panic_entrypoint(tcx) {
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

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

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

compiler/rustc_feature/src/builtin_attrs.rs

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

13271332
BuiltinAttribute {
13281333
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
@@ -1508,6 +1508,9 @@ pub enum AttributeKind {
15081508
/// Represents `#[rustc_offload_kernel]`
15091509
RustcOffloadKernel,
15101510

1511+
/// Represents `#[rustc_panic_entrypoint]`
1512+
RustcPanicEntrypoint,
1513+
15111514
/// Represents `#[rustc_paren_sugar]`.
15121515
RustcParenSugar(Span),
15131516

compiler/rustc_hir/src/attrs/encode_cross_crate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ impl AttributeKind {
166166
RustcObjcClass { .. } => No,
167167
RustcObjcSelector { .. } => No,
168168
RustcOffloadKernel => Yes,
169+
RustcPanicEntrypoint => No,
169170
RustcParenSugar(..) => No,
170171
RustcPassByValue(..) => Yes,
171172
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/internal.rs

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@
44
use rustc_ast as ast;
55
use rustc_ast::{Pat, PatKind, Path};
66
use rustc_hir as hir;
7+
use rustc_hir::attrs::{AttributeKind, InlineAttr};
78
use rustc_hir::def::Res;
89
use rustc_hir::def_id::DefId;
9-
use rustc_hir::{Expr, ExprKind, HirId, find_attr};
10+
use rustc_hir::intravisit::FnKind;
11+
use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, find_attr};
1012
use rustc_middle::ty::{self, GenericArgsRef, PredicatePolarity};
1113
use rustc_session::{declare_lint_pass, declare_tool_lint};
14+
use rustc_span::def_id::LocalDefId;
1215
use rustc_span::hygiene::{ExpnKind, MacroKind};
1316
use rustc_span::{Span, sym};
1417

@@ -713,3 +716,69 @@ impl EarlyLintPass for BadUseOfFindAttr {
713716
}
714717
}
715718
}
719+
720+
declare_tool_lint! {
721+
/// The `missing_panic_entrypoint` lint detects forgotten use of #[rustc_panic_entrypoint].
722+
///
723+
/// This lint is intended to ensure that panic=immediate-abort can function as designed,
724+
/// because it uses #[rustc_panic_entrypoint] to locate functions that should be outlined
725+
/// for other panic modes, and be deleted entirely when immediate-abort is enabled.
726+
///
727+
/// The current design of this lint is expected to have a lot of false positives outside of core
728+
/// and alloc.
729+
pub rustc::MISSING_PANIC_ENTRYPOINT,
730+
Allow,
731+
"detects missing #[rustc_panic_entrypoint]",
732+
report_in_external_macro: true
733+
}
734+
735+
declare_lint_pass!(MissingPanicEntrypoint => [MISSING_PANIC_ENTRYPOINT]);
736+
737+
fn has_panic_entrypoint(attrs: &[hir::Attribute]) -> bool {
738+
attrs
739+
.iter()
740+
.any(|attr| matches!(attr, hir::Attribute::Parsed(AttributeKind::RustcPanicEntrypoint)))
741+
}
742+
743+
fn has_inline_encouragement(attrs: &[hir::Attribute]) -> bool {
744+
attrs.iter().any(|attr| {
745+
matches!(
746+
attr,
747+
hir::Attribute::Parsed(hir::attrs::AttributeKind::Inline(
748+
InlineAttr::Hint | InlineAttr::Always | InlineAttr::Force { .. },
749+
_
750+
))
751+
)
752+
})
753+
}
754+
755+
fn has_rustc_intrinsic(attrs: &[hir::Attribute]) -> bool {
756+
attrs.iter().any(|attr| matches!(attr, hir::Attribute::Parsed(AttributeKind::RustcIntrinsic)))
757+
}
758+
759+
impl<'tcx> LateLintPass<'tcx> for MissingPanicEntrypoint {
760+
fn check_fn(
761+
&mut self,
762+
cx: &LateContext<'tcx>,
763+
_: FnKind<'tcx>,
764+
fn_decl: &'tcx FnDecl<'tcx>,
765+
_: &'tcx Body<'tcx>,
766+
span: Span,
767+
def_id: LocalDefId,
768+
) {
769+
if matches!(fn_decl.output, hir::FnRetTy::Return(hir::Ty { kind: hir::TyKind::Never, .. }))
770+
{
771+
let attrs = cx.tcx.hir_attrs(cx.tcx.local_def_id_to_hir_id(def_id));
772+
if has_rustc_intrinsic(attrs) {
773+
return;
774+
}
775+
if !has_inline_encouragement(attrs) && !has_panic_entrypoint(attrs) {
776+
cx.emit_span_lint(
777+
MISSING_PANIC_ENTRYPOINT,
778+
span,
779+
crate::lints::MissingPanicEntrypoint,
780+
);
781+
}
782+
}
783+
}
784+
}

0 commit comments

Comments
 (0)