Skip to content

Commit 209ede6

Browse files
Auto merge of #143167 - Mark-Simulacrum:inline-mark-nounwind, r=<try>
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. My guess is this implementation is not going to work in practice (too much overhead, not enough value?) but wanted to see what perf looked like.
2 parents 11ad40b + 05dc174 commit 209ede6

18 files changed

+136
-45
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/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: {

tests/mir-opt/inline/unsized_argument.caller.Inline.diff

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
StorageLive(_3);
1414
_3 = move _1;
1515
_4 = copy ((_3.0: std::ptr::Unique<[i32]>).0: std::ptr::NonNull<[i32]>) as *const [i32] (Transmute);
16-
_2 = callee(move (*_4)) -> [return: bb1, unwind: bb3];
16+
- _2 = callee(move (*_4)) -> [return: bb1, unwind: bb3];
17+
+ _2 = callee(move (*_4)) -> [return: bb1, unwind unreachable];
1718
}
1819

1920
bb1: {
20-
drop(_3) -> [return: bb2, unwind: bb4];
21+
- drop(_3) -> [return: bb2, unwind: bb4];
22+
+ drop(_3) -> [return: bb2, unwind: bb3];
2123
}
2224

2325
bb2: {
@@ -28,10 +30,10 @@
2830
}
2931

3032
bb3 (cleanup): {
31-
drop(_3) -> [return: bb4, unwind terminate(cleanup)];
32-
}
33-
34-
bb4 (cleanup): {
33+
- drop(_3) -> [return: bb4, unwind terminate(cleanup)];
34+
- }
35+
-
36+
- bb4 (cleanup): {
3537
resume;
3638
}
3739
}

tests/mir-opt/pre-codegen/deref_nested_borrows.src.GVN.panic-unwind.diff

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
+ nop;
2121
+ _6 = copy (*_1);
2222
_2 = copy (*_6);
23-
_3 = unknown() -> [return: bb1, unwind continue];
23+
_3 = unknown() -> [return: bb1, unwind unreachable];
2424
}
2525

2626
bb1: {

tests/mir-opt/pre-codegen/deref_nested_borrows.src.PreCodegen.after.panic-unwind.mir

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ fn src(_1: &&u8) -> bool {
1515
bb0: {
1616
_2 = copy (*_1);
1717
_3 = copy (*_2);
18-
_4 = unknown() -> [return: bb1, unwind continue];
18+
_4 = unknown() -> [return: bb1, unwind unreachable];
1919
}
2020

2121
bb1: {

tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,9 @@
7676
_7 = copy _9;
7777
StorageLive(_8);
7878
- _8 = copy _1;
79-
- _6 = std::alloc::Global::alloc_impl(move _7, move _8, const false) -> [return: bb5, unwind continue];
79+
- _6 = std::alloc::Global::alloc_impl(move _7, move _8, const false) -> [return: bb5, unwind unreachable];
8080
+ _8 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }};
81-
+ _6 = std::alloc::Global::alloc_impl(copy _9, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }}, const false) -> [return: bb5, unwind continue];
81+
+ _6 = std::alloc::Global::alloc_impl(copy _9, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }}, const false) -> [return: bb5, unwind unreachable];
8282
}
8383

8484
bb5: {

tests/mir-opt/pre-codegen/loops.vec_move.PreCodegen.after.mir

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ fn vec_move(_1: Vec<impl Sized>) -> () {
1919

2020
bb0: {
2121
StorageLive(_2);
22-
_2 = <Vec<impl Sized> as IntoIterator>::into_iter(move _1) -> [return: bb1, unwind continue];
22+
_2 = <Vec<impl Sized> as IntoIterator>::into_iter(move _1) -> [return: bb1, unwind unreachable];
2323
}
2424

2525
bb1: {
@@ -31,12 +31,12 @@ fn vec_move(_1: Vec<impl Sized>) -> () {
3131
bb2: {
3232
StorageLive(_5);
3333
_4 = &mut _3;
34-
_5 = <std::vec::IntoIter<impl Sized> as Iterator>::next(move _4) -> [return: bb3, unwind: bb9];
34+
_5 = <std::vec::IntoIter<impl Sized> as Iterator>::next(move _4) -> [return: bb3, unwind unreachable];
3535
}
3636

3737
bb3: {
3838
_6 = discriminant(_5);
39-
switchInt(move _6) -> [0: bb4, 1: bb6, otherwise: bb8];
39+
switchInt(move _6) -> [0: bb4, 1: bb6, otherwise: bb10];
4040
}
4141

4242
bb4: {
@@ -52,23 +52,23 @@ fn vec_move(_1: Vec<impl Sized>) -> () {
5252

5353
bb6: {
5454
_7 = move ((_5 as Some).0: impl Sized);
55-
_8 = opaque::<impl Sized>(move _7) -> [return: bb7, unwind: bb9];
55+
_8 = opaque::<impl Sized>(move _7) -> [return: bb7, unwind: bb8];
5656
}
5757

5858
bb7: {
5959
StorageDead(_5);
6060
goto -> bb2;
6161
}
6262

63-
bb8: {
64-
unreachable;
63+
bb8 (cleanup): {
64+
drop(_3) -> [return: bb9, unwind terminate(cleanup)];
6565
}
6666

6767
bb9 (cleanup): {
68-
drop(_3) -> [return: bb10, unwind terminate(cleanup)];
68+
resume;
6969
}
7070

71-
bb10 (cleanup): {
72-
resume;
71+
bb10: {
72+
unreachable;
7373
}
7474
}

0 commit comments

Comments
 (0)