Skip to content

Commit 52a36b9

Browse files
Detect unwind-free functions in MIR
This adds analysis as part of inlining that will mark calls to functions with Unwind::Unreachable even if inlining is not possible, so long as their MIR indicates that they cannot unwind. This would ideally persist into the codegen fn attrs, but that will take more work.
1 parent d41e12f commit 52a36b9

19 files changed

+139
-46
lines changed

compiler/rustc_mir_transform/src/inline.rs

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ trait Inliner<'tcx> {
115115

116116
/// Has the caller body been changed?
117117
fn changed(self) -> bool;
118+
fn set_changed(&mut self);
118119

119120
/// Should inlining happen for a given callee?
120121
fn should_inline_for_callee(&self, def_id: DefId) -> bool;
@@ -187,6 +188,10 @@ impl<'tcx> Inliner<'tcx> for ForceInliner<'tcx> {
187188
self.changed
188189
}
189190

191+
fn set_changed(&mut self) {
192+
self.changed = true;
193+
}
194+
190195
fn should_inline_for_callee(&self, def_id: DefId) -> bool {
191196
ForceInline::should_run_pass_for_callee(self.tcx(), def_id)
192197
}
@@ -334,6 +339,10 @@ impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> {
334339
self.changed
335340
}
336341

342+
fn set_changed(&mut self) {
343+
self.changed = true;
344+
}
345+
337346
fn should_inline_for_callee(&self, _: DefId) -> bool {
338347
true
339348
}
@@ -529,10 +538,35 @@ fn process_blocks<'tcx, I: Inliner<'tcx>>(
529538
let span = trace_span!("process_blocks", %callsite.callee, ?bb);
530539
let _guard = span.enter();
531540

532-
match try_inlining(inliner, caller_body, &callsite) {
541+
let mut unwind_unreachable = Err("did not reach analysis");
542+
match try_inlining(inliner, caller_body, &callsite, &mut unwind_unreachable) {
533543
Err(reason) => {
534544
debug!("not-inlined {} [{}]", callsite.callee, reason);
535545
inliner.on_inline_failure(&callsite, reason);
546+
547+
match unwind_unreachable {
548+
Ok(()) => {
549+
if let Some(TerminatorKind::Call { unwind, .. }) =
550+
caller_body[callsite.block].terminator.as_mut().map(|v| &mut v.kind)
551+
{
552+
inliner.set_changed();
553+
tracing::info!("marked {} unwind unreachable", callsite.callee);
554+
*unwind = UnwindAction::Unreachable;
555+
} else {
556+
bug!(
557+
"unexpected terminator: {:?}",
558+
caller_body[callsite.block].terminator
559+
);
560+
}
561+
}
562+
Err(reason) => {
563+
tracing::info!(
564+
"not marking unwind unreachable {}: {}",
565+
callsite.callee,
566+
reason
567+
);
568+
}
569+
}
536570
}
537571
Ok(new_blocks) => {
538572
debug!("inlined {}", callsite.callee);
@@ -595,17 +629,69 @@ fn resolve_callsite<'tcx, I: Inliner<'tcx>>(
595629
None
596630
}
597631

632+
/// Ok indicates yes, Err(reason) otherwise.
633+
fn should_mark_nounwind<'tcx>(_tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> Result<(), &'static str> {
634+
// Unwinds can only start at certain terminators.
635+
for block in body.basic_blocks.iter() {
636+
let unwind = match block.terminator().kind {
637+
// These never unwind.
638+
TerminatorKind::Goto { .. }
639+
| TerminatorKind::SwitchInt { .. }
640+
| TerminatorKind::UnwindTerminate(_)
641+
| TerminatorKind::Return
642+
| TerminatorKind::Unreachable
643+
| TerminatorKind::CoroutineDrop
644+
| TerminatorKind::FalseEdge { .. }
645+
| TerminatorKind::FalseUnwind { .. } => continue,
646+
647+
// Resume will *continue* unwinding, but if there's no other unwinding terminator it
648+
// will never be reached.
649+
TerminatorKind::UnwindResume => continue,
650+
651+
TerminatorKind::Yield { .. } => {
652+
return Err("impl limitation: yield");
653+
}
654+
655+
TerminatorKind::Drop { unwind, .. }
656+
| TerminatorKind::Call { unwind, .. }
657+
| TerminatorKind::Assert { unwind, .. } => unwind,
658+
659+
TerminatorKind::InlineAsm { .. } => return Err("inlineasm"),
660+
661+
TerminatorKind::TailCall { .. } => {
662+
return Err("impl limitation: tail call");
663+
}
664+
};
665+
666+
match unwind {
667+
UnwindAction::Continue => return Err("unwind: continue"),
668+
// cannot unwind
669+
UnwindAction::Unreachable => {}
670+
// cannot unwind either -- will terminate instead
671+
UnwindAction::Terminate(_) => {}
672+
UnwindAction::Cleanup(_) => return Err("unwind: cleanup"),
673+
}
674+
}
675+
676+
// If we didn't find an unwinding terminator, the function cannot unwind.
677+
Ok(())
678+
}
679+
598680
/// Attempts to inline a callsite into the caller body. When successful returns basic blocks
599681
/// containing the inlined body. Otherwise returns an error describing why inlining didn't take
600682
/// place.
601683
fn try_inlining<'tcx, I: Inliner<'tcx>>(
602684
inliner: &I,
603685
caller_body: &mut Body<'tcx>,
604686
callsite: &CallSite<'tcx>,
687+
unwind: &mut Result<(), &'static str>,
605688
) -> Result<std::ops::Range<BasicBlock>, &'static str> {
606689
let tcx = inliner.tcx();
607690
check_mir_is_available(inliner, caller_body, callsite.callee)?;
608691

692+
let callee_body = try_instance_mir(tcx, callsite.callee.def)?;
693+
*unwind = should_mark_nounwind(tcx, callee_body);
694+
609695
let callee_attrs = tcx.codegen_fn_attrs(callsite.callee.def_id());
610696
check_inline::is_inline_valid_on_fn(tcx, callsite.callee.def_id())?;
611697
check_codegen_attributes(inliner, callsite, callee_attrs)?;
@@ -622,7 +708,6 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
622708
}
623709
}
624710

625-
let callee_body = try_instance_mir(tcx, callsite.callee.def)?;
626711
check_inline::is_inline_valid_on_body(tcx, callee_body)?;
627712
inliner.check_callee_mir_body(callsite, callee_body, callee_attrs)?;
628713

tests/codegen/drop.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ impl Drop for SomeUniqueName {
1111
}
1212

1313
#[inline(never)]
14-
pub fn possibly_unwinding() {}
14+
pub fn possibly_unwinding() {
15+
panic!();
16+
}
1517

1618
// CHECK-LABEL: @droppy
1719
#[no_mangle]

tests/codegen/mem-replace-big-type.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub fn replace_big(dst: &mut Big, src: Big) -> Big {
2525
// CHECK-NOT: call void @llvm.memcpy
2626

2727
// For a large type, we expect exactly three `memcpy`s
28-
// CHECK-LABEL: define internal void @{{.+}}mem{{.+}}replace{{.+}}(ptr
28+
// CHECK-LABEL: define void @{{.+}}mem{{.+}}replace{{.+}}(ptr
2929
// CHECK-SAME: sret([56 x i8]){{.+}}[[RESULT:%.+]], ptr{{.+}}%dest, ptr{{.+}}%src)
3030
// CHECK-NOT: call void @llvm.memcpy
3131
// CHECK: call void @llvm.memcpy.{{.+}}(ptr align 8 [[RESULT]], ptr align 8 %dest, i{{.*}} 56, i1 false)

tests/codegen/personality_lifetimes.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ impl Drop for S {
1313
}
1414

1515
#[inline(never)]
16-
fn might_unwind() {}
16+
fn might_unwind() {
17+
panic!();
18+
}
1719

1820
// CHECK-LABEL: @test
1921
#[no_mangle]

tests/mir-opt/inline/caller_with_trivial_bound.foo.Inline.panic-unwind.diff

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
bb0: {
1212
StorageLive(_1);
13-
_1 = bar::<T>() -> [return: bb1, unwind continue];
13+
- _1 = bar::<T>() -> [return: bb1, unwind continue];
14+
+ _1 = bar::<T>() -> [return: bb1, unwind unreachable];
1415
}
1516

1617
bb1: {

tests/mir-opt/inline/exponential_runtime.main.Inline.panic-unwind.diff

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
+ StorageLive(_17);
5757
+ StorageLive(_18);
5858
+ StorageLive(_19);
59-
+ _17 = <() as A>::call() -> [return: bb12, unwind continue];
59+
+ _17 = <() as A>::call() -> [return: bb12, unwind unreachable];
6060
}
6161

6262
bb1: {
@@ -124,11 +124,11 @@
124124
+ }
125125
+
126126
+ bb12: {
127-
+ _18 = <() as A>::call() -> [return: bb13, unwind continue];
127+
+ _18 = <() as A>::call() -> [return: bb13, unwind unreachable];
128128
+ }
129129
+
130130
+ bb13: {
131-
+ _19 = <() as A>::call() -> [return: bb11, unwind continue];
131+
+ _19 = <() as A>::call() -> [return: bb11, unwind unreachable];
132132
}
133133
}
134134

tests/mir-opt/inline/inline_options.main.Inline.after.panic-unwind.mir

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ fn main() -> () {
1212

1313
bb0: {
1414
StorageLive(_1);
15-
_1 = not_inlined() -> [return: bb1, unwind continue];
15+
_1 = not_inlined() -> [return: bb1, unwind unreachable];
1616
}
1717

1818
bb1: {
@@ -21,7 +21,7 @@ fn main() -> () {
2121
StorageLive(_3);
2222
StorageLive(_4);
2323
StorageLive(_5);
24-
_3 = g() -> [return: bb3, unwind continue];
24+
_3 = g() -> [return: bb3, unwind unreachable];
2525
}
2626

2727
bb2: {
@@ -34,10 +34,10 @@ fn main() -> () {
3434
}
3535

3636
bb3: {
37-
_4 = g() -> [return: bb4, unwind continue];
37+
_4 = g() -> [return: bb4, unwind unreachable];
3838
}
3939

4040
bb4: {
41-
_5 = g() -> [return: bb2, unwind continue];
41+
_5 = g() -> [return: bb2, unwind unreachable];
4242
}
4343
}

tests/mir-opt/inline/issue_106141.outer.Inline.panic-unwind.diff

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
+ StorageLive(_1);
2121
+ StorageLive(_2);
2222
+ _1 = const inner::promoted[0];
23-
+ _0 = index() -> [return: bb1, unwind continue];
23+
+ _0 = index() -> [return: bb1, unwind unreachable];
2424
}
2525

2626
bb1: {

tests/mir-opt/inline/rustc_no_mir_inline.caller.Inline.panic-unwind.diff

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77

88
bb0: {
99
StorageLive(_1);
10-
_1 = callee() -> [return: bb1, unwind continue];
10+
- _1 = callee() -> [return: bb1, unwind continue];
11+
+ _1 = callee() -> [return: bb1, unwind unreachable];
1112
}
1213

1314
bb1: {

tests/mir-opt/inline/rustc_no_mir_inline.caller.PreCodegen.after.panic-unwind.mir

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ fn caller() -> () {
55
let _1: ();
66

77
bb0: {
8-
_1 = callee() -> [return: bb1, unwind continue];
8+
_1 = callee() -> [return: bb1, unwind unreachable];
99
}
1010

1111
bb1: {

0 commit comments

Comments
 (0)