Skip to content

Commit 8a834d7

Browse files
committed
Add rustc_panic_entrypoint
1 parent d1d3ef4 commit 8a834d7

44 files changed

Lines changed: 287 additions & 168 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
@@ -1143,6 +1143,15 @@ impl<S: Stage> NoArgsAttributeParser<S> for RustcNonnullOptimizationGuaranteedPa
11431143
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNonnullOptimizationGuaranteed;
11441144
}
11451145

1146+
pub(crate) struct RustcPanicEntrypointParser;
1147+
1148+
impl<S: Stage> NoArgsAttributeParser<S> for RustcPanicEntrypointParser {
1149+
const PATH: &[Symbol] = &[sym::rustc_panic_entrypoint];
1150+
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
1151+
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
1152+
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcPanicEntrypoint;
1153+
}
1154+
11461155
pub(crate) struct RustcStrictCoherenceParser;
11471156

11481157
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
@@ -318,6 +318,7 @@ attribute_parsers!(
318318
Single<WithoutArgs<RustcNonnullOptimizationGuaranteedParser>>,
319319
Single<WithoutArgs<RustcNounwindParser>>,
320320
Single<WithoutArgs<RustcOffloadKernelParser>>,
321+
Single<WithoutArgs<RustcPanicEntrypointParser>>,
321322
Single<WithoutArgs<RustcParenSugarParser>>,
322323
Single<WithoutArgs<RustcPassByValueParser>>,
323324
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::{
@@ -293,6 +293,9 @@ fn process_builtin_attrs(
293293
codegen_fn_attrs.patchable_function_entry =
294294
Some(PatchableFunctionEntry::from_prefix_and_entry(*prefix, *entry));
295295
}
296+
AttributeKind::RustcPanicEntrypoint => {
297+
codegen_fn_attrs.flags |= CodegenFnAttrFlags::PANIC_ENTRYPOINT
298+
}
296299
_ => {}
297300
}
298301
}
@@ -385,6 +388,28 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code
385388
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
386389
}
387390
}
391+
392+
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::PANIC_ENTRYPOINT) {
393+
// Panic entrypoints are always cold.
394+
//
395+
// If we have immediate-abort enabled, we want panic entrypoints to be inlined. We have a
396+
// MIR transform that tries to patch out immediate-abort panic entrypoint calls, but it can
397+
// miss indirect calls. In such cases, this should encourage the optimizer to clean up the
398+
// mess that we missed.
399+
//
400+
// When the panic strategies that support panic messages are enabled, we want panic
401+
// entrypoints outlined and optimized for size.
402+
// Most panic entrypoints want #[track_caller] but not all, so we do not add it.
403+
// FIXME: It seems unlikely that all choices of #[track_caller] were deliberate, but it can
404+
// have a code size impact, so it makes sense to apply judiciously.
405+
codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD;
406+
if tcx.sess.panic_strategy() == PanicStrategy::ImmediateAbort {
407+
codegen_fn_attrs.inline = InlineAttr::Always;
408+
} else {
409+
codegen_fn_attrs.inline = InlineAttr::Never;
410+
codegen_fn_attrs.optimize = OptimizeAttr::Size;
411+
}
412+
}
388413
}
389414

390415
#[derive(Diagnostic)]

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use rustc_middle::{bug, span_bug};
1414
use rustc_session::config::OptLevel;
1515
use rustc_span::{Span, Spanned};
1616
use rustc_target::callconv::{ArgAbi, ArgAttributes, CastTarget, FnAbi, PassMode};
17+
use rustc_target::spec::PanicStrategy;
1718
use tracing::{debug, info};
1819

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

@@ -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
@@ -1527,6 +1527,9 @@ pub enum AttributeKind {
15271527
/// Represents `#[rustc_offload_kernel]`
15281528
RustcOffloadKernel,
15291529

1530+
/// Represents `#[rustc_panic_entrypoint]`
1531+
RustcPanicEntrypoint,
1532+
15301533
/// Represents `#[rustc_paren_sugar]`.
15311534
RustcParenSugar(Span),
15321535

compiler/rustc_hir/src/attrs/encode_cross_crate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ impl AttributeKind {
170170
RustcObjcClass { .. } => No,
171171
RustcObjcSelector { .. } => No,
172172
RustcOffloadKernel => Yes,
173+
RustcPanicEntrypoint => No,
173174
RustcParenSugar(..) => No,
174175
RustcPassByValue(..) => Yes,
175176
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

@@ -800,3 +803,69 @@ impl<'tcx> LateLintPass<'tcx> for RustcMustMatchExhaustively {
800803
}
801804
}
802805
}
806+
807+
declare_tool_lint! {
808+
/// The `missing_panic_entrypoint` lint detects forgotten use of #[rustc_panic_entrypoint].
809+
///
810+
/// This lint is intended to ensure that panic=immediate-abort can function as designed,
811+
/// because it uses #[rustc_panic_entrypoint] to locate functions that should be outlined
812+
/// for other panic modes, and be deleted entirely when immediate-abort is enabled.
813+
///
814+
/// The current design of this lint is expected to have a lot of false positives outside of core
815+
/// and alloc.
816+
pub rustc::MISSING_PANIC_ENTRYPOINT,
817+
Allow,
818+
"detects missing #[rustc_panic_entrypoint]",
819+
report_in_external_macro: true
820+
}
821+
822+
declare_lint_pass!(MissingPanicEntrypoint => [MISSING_PANIC_ENTRYPOINT]);
823+
824+
fn has_panic_entrypoint(attrs: &[hir::Attribute]) -> bool {
825+
attrs
826+
.iter()
827+
.any(|attr| matches!(attr, hir::Attribute::Parsed(AttributeKind::RustcPanicEntrypoint)))
828+
}
829+
830+
fn has_inline_encouragement(attrs: &[hir::Attribute]) -> bool {
831+
attrs.iter().any(|attr| {
832+
matches!(
833+
attr,
834+
hir::Attribute::Parsed(hir::attrs::AttributeKind::Inline(
835+
InlineAttr::Hint | InlineAttr::Always | InlineAttr::Force { .. },
836+
_
837+
))
838+
)
839+
})
840+
}
841+
842+
fn has_rustc_intrinsic(attrs: &[hir::Attribute]) -> bool {
843+
attrs.iter().any(|attr| matches!(attr, hir::Attribute::Parsed(AttributeKind::RustcIntrinsic)))
844+
}
845+
846+
impl<'tcx> LateLintPass<'tcx> for MissingPanicEntrypoint {
847+
fn check_fn(
848+
&mut self,
849+
cx: &LateContext<'tcx>,
850+
_: FnKind<'tcx>,
851+
fn_decl: &'tcx FnDecl<'tcx>,
852+
_: &'tcx Body<'tcx>,
853+
span: Span,
854+
def_id: LocalDefId,
855+
) {
856+
if matches!(fn_decl.output, hir::FnRetTy::Return(hir::Ty { kind: hir::TyKind::Never, .. }))
857+
{
858+
let attrs = cx.tcx.hir_attrs(cx.tcx.local_def_id_to_hir_id(def_id));
859+
if has_rustc_intrinsic(attrs) {
860+
return;
861+
}
862+
if !has_inline_encouragement(attrs) && !has_panic_entrypoint(attrs) {
863+
cx.emit_span_lint(
864+
MISSING_PANIC_ENTRYPOINT,
865+
span,
866+
crate::lints::MissingPanicEntrypoint,
867+
);
868+
}
869+
}
870+
}
871+
}

0 commit comments

Comments
 (0)