1
Fork 0

Inline FnOnce once again

This commit is contained in:
Michael Goulet 2025-03-03 02:12:28 +00:00
parent e081b7b77e
commit d33946c3ab
23 changed files with 391 additions and 116 deletions

View file

@ -625,13 +625,6 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
return Err("implementation limitation -- return type mismatch"); return Err("implementation limitation -- return type mismatch");
} }
if callsite.fn_sig.abi() == ExternAbi::RustCall { if callsite.fn_sig.abi() == ExternAbi::RustCall {
// FIXME: Don't inline user-written `extern "rust-call"` functions,
// since this is generally perf-negative on rustc, and we hope that
// LLVM will inline these functions instead.
if callee_body.spread_arg.is_some() {
return Err("user-written rust-call functions");
}
let (self_arg, arg_tuple) = match &args[..] { let (self_arg, arg_tuple) = match &args[..] {
[arg_tuple] => (None, arg_tuple), [arg_tuple] => (None, arg_tuple),
[self_arg, arg_tuple] => (Some(self_arg), arg_tuple), [self_arg, arg_tuple] => (Some(self_arg), arg_tuple),
@ -641,18 +634,23 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
let self_arg_ty = self_arg.map(|self_arg| self_arg.node.ty(&caller_body.local_decls, tcx)); let self_arg_ty = self_arg.map(|self_arg| self_arg.node.ty(&caller_body.local_decls, tcx));
let arg_tuple_ty = arg_tuple.node.ty(&caller_body.local_decls, tcx); let arg_tuple_ty = arg_tuple.node.ty(&caller_body.local_decls, tcx);
let arg_tys = if callee_body.spread_arg.is_some() {
std::slice::from_ref(&arg_tuple_ty)
} else {
let ty::Tuple(arg_tuple_tys) = *arg_tuple_ty.kind() else { let ty::Tuple(arg_tuple_tys) = *arg_tuple_ty.kind() else {
bug!("Closure arguments are not passed as a tuple"); bug!("Closure arguments are not passed as a tuple");
}; };
arg_tuple_tys.as_slice()
};
for (arg_ty, input) in for (arg_ty, input) in
self_arg_ty.into_iter().chain(arg_tuple_tys).zip(callee_body.args_iter()) self_arg_ty.into_iter().chain(arg_tys.iter().copied()).zip(callee_body.args_iter())
{ {
let input_type = callee_body.local_decls[input].ty; let input_type = callee_body.local_decls[input].ty;
if !util::sub_types(tcx, inliner.typing_env(), input_type, arg_ty) { if !util::sub_types(tcx, inliner.typing_env(), input_type, arg_ty) {
trace!(?arg_ty, ?input_type); trace!(?arg_ty, ?input_type);
debug!("failed to normalize tuple argument type"); debug!("failed to normalize tuple argument type");
return Err("implementation limitation -- arg mismatch"); return Err("implementation limitation");
} }
} }
} else { } else {
@ -1059,8 +1057,7 @@ fn make_call_args<'tcx, I: Inliner<'tcx>>(
closure_ref_arg.chain(tuple_tmp_args).collect() closure_ref_arg.chain(tuple_tmp_args).collect()
} else { } else {
// FIXME(edition_2024): switch back to a normal method call. args.into_iter()
<_>::into_iter(args)
.map(|a| create_temp_if_necessary(inliner, a.node, callsite, caller_body, return_block)) .map(|a| create_temp_if_necessary(inliner, a.node, callsite, caller_body, return_block))
.collect() .collect()
} }

View file

@ -7,23 +7,42 @@
let mut _0: (); let mut _0: ();
let mut _3: &mut std::boxed::Box<dyn std::ops::FnMut<I, Output = ()>>; let mut _3: &mut std::boxed::Box<dyn std::ops::FnMut<I, Output = ()>>;
let mut _4: I; let mut _4: I;
+ scope 1 (inlined <Box<dyn FnMut<I, Output = ()>> as FnMut<I>>::call_mut) {
+ let mut _5: &mut dyn std::ops::FnMut<I, Output = ()>;
+ let mut _6: std::boxed::Box<dyn std::ops::FnMut<I, Output = ()>>;
+ let mut _7: *const dyn std::ops::FnMut<I, Output = ()>;
+ }
bb0: { bb0: {
StorageLive(_3); StorageLive(_3);
_3 = &mut _1; _3 = &mut _1;
StorageLive(_4); StorageLive(_4);
_4 = move _2; _4 = move _2;
_0 = <Box<dyn FnMut<I, Output = ()>> as FnMut<I>>::call_mut(move _3, move _4) -> [return: bb1, unwind unreachable]; - _0 = <Box<dyn FnMut<I, Output = ()>> as FnMut<I>>::call_mut(move _3, move _4) -> [return: bb1, unwind unreachable];
+ StorageLive(_6);
+ StorageLive(_7);
+ StorageLive(_5);
+ _6 = copy (*_3);
+ _7 = copy ((_6.0: std::ptr::Unique<dyn std::ops::FnMut<I, Output = ()>>).0: std::ptr::NonNull<dyn std::ops::FnMut<I, Output = ()>>) as *const dyn std::ops::FnMut<I, Output = ()> (Transmute);
+ _5 = &mut (*_7);
+ _0 = <dyn FnMut<I, Output = ()> as FnMut<I>>::call_mut(move _5, move _4) -> [return: bb2, unwind unreachable];
} }
bb1: { bb1: {
StorageDead(_4); - StorageDead(_4);
StorageDead(_3); - StorageDead(_3);
drop(_1) -> [return: bb2, unwind unreachable]; - drop(_1) -> [return: bb2, unwind unreachable];
+ return;
} }
bb2: { bb2: {
return; - return;
+ StorageDead(_5);
+ StorageDead(_7);
+ StorageDead(_6);
+ StorageDead(_4);
+ StorageDead(_3);
+ drop(_1) -> [return: bb1, unwind unreachable];
} }
} }

View file

@ -7,31 +7,54 @@
let mut _0: (); let mut _0: ();
let mut _3: &mut std::boxed::Box<dyn std::ops::FnMut<I, Output = ()>>; let mut _3: &mut std::boxed::Box<dyn std::ops::FnMut<I, Output = ()>>;
let mut _4: I; let mut _4: I;
+ scope 1 (inlined <Box<dyn FnMut<I, Output = ()>> as FnMut<I>>::call_mut) {
+ let mut _5: &mut dyn std::ops::FnMut<I, Output = ()>;
+ let mut _6: std::boxed::Box<dyn std::ops::FnMut<I, Output = ()>>;
+ let mut _7: *const dyn std::ops::FnMut<I, Output = ()>;
+ }
bb0: { bb0: {
StorageLive(_3); StorageLive(_3);
_3 = &mut _1; _3 = &mut _1;
StorageLive(_4); StorageLive(_4);
_4 = move _2; _4 = move _2;
_0 = <Box<dyn FnMut<I, Output = ()>> as FnMut<I>>::call_mut(move _3, move _4) -> [return: bb1, unwind: bb3]; - _0 = <Box<dyn FnMut<I, Output = ()>> as FnMut<I>>::call_mut(move _3, move _4) -> [return: bb1, unwind: bb3];
+ StorageLive(_6);
+ StorageLive(_7);
+ StorageLive(_5);
+ _6 = copy (*_3);
+ _7 = copy ((_6.0: std::ptr::Unique<dyn std::ops::FnMut<I, Output = ()>>).0: std::ptr::NonNull<dyn std::ops::FnMut<I, Output = ()>>) as *const dyn std::ops::FnMut<I, Output = ()> (Transmute);
+ _5 = &mut (*_7);
+ _0 = <dyn FnMut<I, Output = ()> as FnMut<I>>::call_mut(move _5, move _4) -> [return: bb4, unwind: bb2];
} }
bb1: { bb1: {
StorageDead(_4); - StorageDead(_4);
StorageDead(_3); - StorageDead(_3);
drop(_1) -> [return: bb2, unwind: bb4]; - drop(_1) -> [return: bb2, unwind: bb4];
+ return;
} }
bb2: { - bb2: {
return; - return;
+ bb2 (cleanup): {
+ drop(_1) -> [return: bb3, unwind terminate(cleanup)];
} }
bb3 (cleanup): { bb3 (cleanup): {
drop(_1) -> [return: bb4, unwind terminate(cleanup)]; - drop(_1) -> [return: bb4, unwind terminate(cleanup)];
+ resume;
} }
bb4 (cleanup): { - bb4 (cleanup): {
resume; - resume;
+ bb4: {
+ StorageDead(_5);
+ StorageDead(_7);
+ StorageDead(_6);
+ StorageDead(_4);
+ StorageDead(_3);
+ drop(_1) -> [return: bb1, unwind: bb3];
} }
} }

View file

@ -8,6 +8,6 @@ use std::marker::Tuple;
// EMIT_MIR dont_ice_on_generic_rust_call.call.Inline.diff // EMIT_MIR dont_ice_on_generic_rust_call.call.Inline.diff
pub fn call<I: Tuple>(mut mock: Box<dyn FnMut<I, Output = ()>>, input: I) { pub fn call<I: Tuple>(mut mock: Box<dyn FnMut<I, Output = ()>>, input: I) {
// CHECK-LABEL: fn call( // CHECK-LABEL: fn call(
// CHECK-NOT: inlined // CHECK: (inlined <Box<dyn FnMut<I, Output = ()>> as FnMut<I>>::call_mut)
mock.call_mut(input) mock.call_mut(input)
} }

View file

@ -7,6 +7,11 @@
let _2: (); let _2: ();
let mut _3: &std::boxed::Box<dyn std::ops::Fn(i32)>; let mut _3: &std::boxed::Box<dyn std::ops::Fn(i32)>;
let mut _4: (i32,); let mut _4: (i32,);
+ scope 1 (inlined <Box<dyn Fn(i32)> as Fn<(i32,)>>::call) {
+ let mut _5: &dyn std::ops::Fn(i32);
+ let mut _6: std::boxed::Box<dyn std::ops::Fn(i32)>;
+ let mut _7: *const dyn std::ops::Fn(i32);
+ }
bb0: { bb0: {
StorageLive(_2); StorageLive(_2);
@ -14,19 +19,34 @@
_3 = &_1; _3 = &_1;
StorageLive(_4); StorageLive(_4);
_4 = (const 1_i32,); _4 = (const 1_i32,);
_2 = <Box<dyn Fn(i32)> as Fn<(i32,)>>::call(move _3, move _4) -> [return: bb1, unwind unreachable]; - _2 = <Box<dyn Fn(i32)> as Fn<(i32,)>>::call(move _3, move _4) -> [return: bb1, unwind unreachable];
+ StorageLive(_6);
+ StorageLive(_7);
+ StorageLive(_5);
+ _6 = copy (*_3);
+ _7 = copy ((_6.0: std::ptr::Unique<dyn std::ops::Fn(i32)>).0: std::ptr::NonNull<dyn std::ops::Fn(i32)>) as *const dyn std::ops::Fn(i32) (Transmute);
+ _5 = &(*_7);
+ _2 = <dyn Fn(i32) as Fn<(i32,)>>::call(move _5, move _4) -> [return: bb2, unwind unreachable];
} }
bb1: { bb1: {
+ return;
+ }
+
+ bb2: {
+ StorageDead(_5);
+ StorageDead(_7);
+ StorageDead(_6);
StorageDead(_4); StorageDead(_4);
StorageDead(_3); StorageDead(_3);
StorageDead(_2); StorageDead(_2);
_0 = const (); _0 = const ();
drop(_1) -> [return: bb2, unwind unreachable]; - drop(_1) -> [return: bb2, unwind unreachable];
} - }
-
bb2: { - bb2: {
return; - return;
+ drop(_1) -> [return: bb1, unwind unreachable];
} }
} }

View file

@ -7,6 +7,11 @@
let _2: (); let _2: ();
let mut _3: &std::boxed::Box<dyn std::ops::Fn(i32)>; let mut _3: &std::boxed::Box<dyn std::ops::Fn(i32)>;
let mut _4: (i32,); let mut _4: (i32,);
+ scope 1 (inlined <Box<dyn Fn(i32)> as Fn<(i32,)>>::call) {
+ let mut _5: &dyn std::ops::Fn(i32);
+ let mut _6: std::boxed::Box<dyn std::ops::Fn(i32)>;
+ let mut _7: *const dyn std::ops::Fn(i32);
+ }
bb0: { bb0: {
StorageLive(_2); StorageLive(_2);
@ -14,27 +19,47 @@
_3 = &_1; _3 = &_1;
StorageLive(_4); StorageLive(_4);
_4 = (const 1_i32,); _4 = (const 1_i32,);
_2 = <Box<dyn Fn(i32)> as Fn<(i32,)>>::call(move _3, move _4) -> [return: bb1, unwind: bb3]; - _2 = <Box<dyn Fn(i32)> as Fn<(i32,)>>::call(move _3, move _4) -> [return: bb1, unwind: bb3];
+ StorageLive(_6);
+ StorageLive(_7);
+ StorageLive(_5);
+ _6 = copy (*_3);
+ _7 = copy ((_6.0: std::ptr::Unique<dyn std::ops::Fn(i32)>).0: std::ptr::NonNull<dyn std::ops::Fn(i32)>) as *const dyn std::ops::Fn(i32) (Transmute);
+ _5 = &(*_7);
+ _2 = <dyn Fn(i32) as Fn<(i32,)>>::call(move _5, move _4) -> [return: bb4, unwind: bb2];
} }
bb1: { bb1: {
StorageDead(_4); - StorageDead(_4);
StorageDead(_3); - StorageDead(_3);
StorageDead(_2); - StorageDead(_2);
_0 = const (); - _0 = const ();
drop(_1) -> [return: bb2, unwind: bb4]; - drop(_1) -> [return: bb2, unwind: bb4];
+ return;
} }
bb2: { - bb2: {
return; - return;
+ bb2 (cleanup): {
+ drop(_1) -> [return: bb3, unwind terminate(cleanup)];
} }
bb3 (cleanup): { bb3 (cleanup): {
drop(_1) -> [return: bb4, unwind terminate(cleanup)]; - drop(_1) -> [return: bb4, unwind terminate(cleanup)];
+ resume;
} }
bb4 (cleanup): { - bb4 (cleanup): {
resume; - resume;
+ bb4: {
+ StorageDead(_5);
+ StorageDead(_7);
+ StorageDead(_6);
+ StorageDead(_4);
+ StorageDead(_3);
+ StorageDead(_2);
+ _0 = const ();
+ drop(_1) -> [return: bb1, unwind: bb3];
} }
} }

View file

@ -5,6 +5,6 @@
// EMIT_MIR inline_box_fn.call.Inline.diff // EMIT_MIR inline_box_fn.call.Inline.diff
fn call(x: Box<dyn Fn(i32)>) { fn call(x: Box<dyn Fn(i32)>) {
// CHECK-LABEL: fn call( // CHECK-LABEL: fn call(
// CHECK-NOT: inlined // CHECK: (inlined <Box<dyn Fn(i32)> as Fn<(i32,)>>::call)
x(1); x(1);
} }

View file

@ -5,9 +5,15 @@
let mut _0: (); let mut _0: ();
let _1: (); let _1: ();
+ let mut _2: fn() {f}; + let mut _2: fn() {f};
+ let mut _4: ();
+ scope 1 (inlined call::<fn() {f}>) { + scope 1 (inlined call::<fn() {f}>) {
+ debug f => _2; + debug f => _2;
+ let _3: (); + let _3: ();
+ scope 2 (inlined <fn() {f} as FnOnce<()>>::call_once - shim(fn() {f})) {
+ scope 3 (inlined f) {
+ let _5: ();
+ }
+ }
+ } + }
bb0: { bb0: {
@ -16,10 +22,15 @@
+ StorageLive(_2); + StorageLive(_2);
+ _2 = f; + _2 = f;
+ StorageLive(_3); + StorageLive(_3);
+ _3 = <fn() {f} as FnOnce<()>>::call_once(move _2, const ()) -> [return: bb1, unwind unreachable]; + StorageLive(_4);
+ _4 = const ();
+ StorageLive(_5);
+ _5 = call::<fn() {f}>(f) -> [return: bb1, unwind unreachable];
} }
bb1: { bb1: {
+ StorageDead(_5);
+ StorageDead(_4);
+ StorageDead(_3); + StorageDead(_3);
+ StorageDead(_2); + StorageDead(_2);
StorageDead(_1); StorageDead(_1);

View file

@ -5,9 +5,15 @@
let mut _0: (); let mut _0: ();
let _1: (); let _1: ();
+ let mut _2: fn() {f}; + let mut _2: fn() {f};
+ let mut _4: ();
+ scope 1 (inlined call::<fn() {f}>) { + scope 1 (inlined call::<fn() {f}>) {
+ debug f => _2; + debug f => _2;
+ let _3: (); + let _3: ();
+ scope 2 (inlined <fn() {f} as FnOnce<()>>::call_once - shim(fn() {f})) {
+ scope 3 (inlined f) {
+ let _5: ();
+ }
+ }
+ } + }
bb0: { bb0: {
@ -16,10 +22,15 @@
+ StorageLive(_2); + StorageLive(_2);
+ _2 = f; + _2 = f;
+ StorageLive(_3); + StorageLive(_3);
+ _3 = <fn() {f} as FnOnce<()>>::call_once(move _2, const ()) -> [return: bb1, unwind continue]; + StorageLive(_4);
+ _4 = const ();
+ StorageLive(_5);
+ _5 = call::<fn() {f}>(f) -> [return: bb1, unwind continue];
} }
bb1: { bb1: {
+ StorageDead(_5);
+ StorageDead(_4);
+ StorageDead(_3); + StorageDead(_3);
+ StorageDead(_2); + StorageDead(_2);
StorageDead(_1); StorageDead(_1);

View file

@ -5,6 +5,7 @@
let mut _0: (); let mut _0: ();
let _1: (!, !); let _1: (!, !);
+ let mut _2: fn() -> ! {sleep}; + let mut _2: fn() -> ! {sleep};
+ let mut _7: ();
+ scope 1 (inlined call_twice::<!, fn() -> ! {sleep}>) { + scope 1 (inlined call_twice::<!, fn() -> ! {sleep}>) {
+ debug f => _2; + debug f => _2;
+ let mut _3: &fn() -> ! {sleep}; + let mut _3: &fn() -> ! {sleep};
@ -17,6 +18,10 @@
+ debug b => _6; + debug b => _6;
+ } + }
+ } + }
+ scope 4 (inlined <fn() -> ! {sleep} as Fn<()>>::call - shim(fn() -> ! {sleep})) {
+ scope 5 (inlined sleep) {
+ }
+ }
+ } + }
bb0: { bb0: {
@ -28,24 +33,13 @@
+ StorageLive(_6); + StorageLive(_6);
+ StorageLive(_3); + StorageLive(_3);
+ _3 = &_2; + _3 = &_2;
+ _4 = <fn() -> ! {sleep} as Fn<()>>::call(move _3, const ()) -> [return: bb1, unwind unreachable]; + StorageLive(_7);
+ _7 = const ();
+ goto -> bb1;
+ } + }
+ +
+ bb1: { + bb1: {
+ StorageDead(_3); + goto -> bb1;
+ StorageLive(_5);
+ _5 = &_2;
+ _6 = <fn() -> ! {sleep} as Fn<()>>::call(move _5, const ()) -> [return: bb2, unwind unreachable];
+ }
+
+ bb2: {
+ StorageDead(_5);
+ _1 = (copy _4, copy _6);
+ drop(_2) -> [return: bb3, unwind unreachable];
+ }
+
+ bb3: {
+ unreachable;
} }
} }

View file

@ -5,6 +5,7 @@
let mut _0: (); let mut _0: ();
let _1: (!, !); let _1: (!, !);
+ let mut _2: fn() -> ! {sleep}; + let mut _2: fn() -> ! {sleep};
+ let mut _8: ();
+ scope 1 (inlined call_twice::<!, fn() -> ! {sleep}>) { + scope 1 (inlined call_twice::<!, fn() -> ! {sleep}>) {
+ debug f => _2; + debug f => _2;
+ let mut _3: &fn() -> ! {sleep}; + let mut _3: &fn() -> ! {sleep};
@ -18,6 +19,10 @@
+ debug b => _6; + debug b => _6;
+ } + }
+ } + }
+ scope 4 (inlined <fn() -> ! {sleep} as Fn<()>>::call - shim(fn() -> ! {sleep})) {
+ scope 5 (inlined sleep) {
+ }
+ }
+ } + }
bb0: { bb0: {
@ -29,40 +34,13 @@
+ StorageLive(_4); + StorageLive(_4);
+ StorageLive(_3); + StorageLive(_3);
+ _3 = &_2; + _3 = &_2;
+ _4 = <fn() -> ! {sleep} as Fn<()>>::call(move _3, const ()) -> [return: bb1, unwind: bb5]; + StorageLive(_8);
+ _8 = const ();
+ goto -> bb1;
+ } + }
+ +
+ bb1: { + bb1: {
+ StorageDead(_3); + goto -> bb1;
+ StorageLive(_5);
+ _5 = &_2;
+ _6 = <fn() -> ! {sleep} as Fn<()>>::call(move _5, const ()) -> [return: bb2, unwind: bb4];
+ }
+
+ bb2: {
+ StorageDead(_5);
+ StorageLive(_7);
+ _7 = move _4;
+ _1 = (move _7, copy _6);
+ StorageDead(_7);
+ StorageDead(_4);
+ drop(_2) -> [return: bb3, unwind continue];
+ }
+
+ bb3: {
+ unreachable;
+ }
+
+ bb4 (cleanup): {
+ drop(_4) -> [return: bb5, unwind terminate(cleanup)];
+ }
+
+ bb5 (cleanup): {
+ drop(_2) -> [return: bb6, unwind terminate(cleanup)];
+ }
+
+ bb6 (cleanup): {
+ resume;
} }
} }

View file

@ -26,7 +26,8 @@ pub fn g(i: i32) -> u32 {
pub fn h() { pub fn h() {
// CHECK-LABEL: fn h( // CHECK-LABEL: fn h(
// CHECK: (inlined call_twice::<!, fn() -> ! {sleep}>) // CHECK: (inlined call_twice::<!, fn() -> ! {sleep}>)
// CHECK-NOT: inlined // CHECK: (inlined <fn() -> ! {sleep} as Fn<()>>::call - shim(fn() -> ! {sleep}))
// CHECK: (inlined sleep)
call_twice(sleep); call_twice(sleep);
} }

View file

@ -9,6 +9,10 @@
let _4: fn() {foo}; let _4: fn() {foo};
let mut _5: (); let mut _5: ();
+ scope 1 (inlined hide_foo) { + scope 1 (inlined hide_foo) {
+ }
+ scope 2 (inlined <fn() {foo} as Fn<()>>::call - shim(fn() {foo})) {
+ scope 3 (inlined foo) {
+ }
+ } + }
bb0: { bb0: {
@ -23,22 +27,20 @@
StorageLive(_5); StorageLive(_5);
_5 = (); _5 = ();
- _2 = <fn() {foo} as Fn<()>>::call(move _3, move _5) -> [return: bb2, unwind unreachable]; - _2 = <fn() {foo} as Fn<()>>::call(move _3, move _5) -> [return: bb2, unwind unreachable];
+ _2 = <fn() {foo} as Fn<()>>::call(move _3, move _5) -> [return: bb1, unwind unreachable]; - }
} -
- bb2: { - bb2: {
+ bb1: {
StorageDead(_5); StorageDead(_5);
StorageDead(_3); StorageDead(_3);
StorageDead(_4); StorageDead(_4);
StorageDead(_2); StorageDead(_2);
_0 = const (); _0 = const ();
- drop(_1) -> [return: bb3, unwind unreachable]; - drop(_1) -> [return: bb3, unwind unreachable];
+ drop(_1) -> [return: bb2, unwind unreachable]; + drop(_1) -> [return: bb1, unwind unreachable];
} }
- bb3: { - bb3: {
+ bb2: { + bb1: {
return; return;
} }
} }

View file

@ -9,6 +9,10 @@
let _4: fn() {foo}; let _4: fn() {foo};
let mut _5: (); let mut _5: ();
+ scope 1 (inlined hide_foo) { + scope 1 (inlined hide_foo) {
+ }
+ scope 2 (inlined <fn() {foo} as Fn<()>>::call - shim(fn() {foo})) {
+ scope 3 (inlined foo) {
+ }
+ } + }
bb0: { bb0: {
@ -23,33 +27,29 @@
StorageLive(_5); StorageLive(_5);
_5 = (); _5 = ();
- _2 = <fn() {foo} as Fn<()>>::call(move _3, move _5) -> [return: bb2, unwind: bb4]; - _2 = <fn() {foo} as Fn<()>>::call(move _3, move _5) -> [return: bb2, unwind: bb4];
+ _2 = <fn() {foo} as Fn<()>>::call(move _3, move _5) -> [return: bb1, unwind: bb3]; - }
} -
- bb2: { - bb2: {
+ bb1: {
StorageDead(_5); StorageDead(_5);
StorageDead(_3); StorageDead(_3);
StorageDead(_4); StorageDead(_4);
StorageDead(_2); StorageDead(_2);
_0 = const (); _0 = const ();
- drop(_1) -> [return: bb3, unwind: bb5]; - drop(_1) -> [return: bb3, unwind: bb5];
+ drop(_1) -> [return: bb2, unwind: bb4]; + drop(_1) -> [return: bb1, unwind: bb2];
} }
- bb3: { - bb3: {
+ bb2: { + bb1: {
return; return;
} }
- bb4 (cleanup): { - bb4 (cleanup): {
- drop(_1) -> [return: bb5, unwind terminate(cleanup)]; - drop(_1) -> [return: bb5, unwind terminate(cleanup)];
+ bb3 (cleanup): { - }
+ drop(_1) -> [return: bb4, unwind terminate(cleanup)]; -
}
- bb5 (cleanup): { - bb5 (cleanup): {
+ bb4 (cleanup): { + bb2 (cleanup): {
resume; resume;
} }
} }

View file

@ -9,10 +9,9 @@ pub fn bar<P>(
_baz: P, _baz: P,
) { ) {
// CHECK-LABEL: fn bar( // CHECK-LABEL: fn bar(
// CHECK: let mut {{.*}}: &fn() {foo};
// CHECK: let {{.*}}: fn() {foo};
// CHECK: (inlined hide_foo) // CHECK: (inlined hide_foo)
// CHECK-NOT: inlined // CHECK: (inlined <fn() {foo} as Fn<()>>::call - shim(fn() {foo}))
// CHECK: (inlined foo)
hide_foo()(); hide_foo()();
} }

View file

@ -0,0 +1,19 @@
//@ test-mir-pass: Inline
//@ compile-flags: --crate-type=lib -C panic=abort
// EMIT_MIR inline_fn_call_for_fn_def.test.Inline.diff
fn inline_fn(x: impl FnOnce() -> i32) -> i32 {
x()
}
fn yield_number() -> i32 {
64
}
fn test() -> i32 {
// CHECK: (inlined inline_fn::<fn() -> i32 {yield_number}>)
// CHECK: (inlined <fn() -> i32 {yield_number} as FnOnce<()>>::call_once - shim(fn() -> i32 {yield_number}))
// CHECK: (inlined yield_number)
inline_fn(yield_number)
}

View file

@ -0,0 +1,34 @@
- // MIR for `test` before Inline
+ // MIR for `test` after Inline
fn test() -> i32 {
let mut _0: i32;
-
- bb0: {
- _0 = inline_fn::<fn() -> i32 {yield_number}>(yield_number) -> [return: bb1, unwind unreachable];
+ let mut _1: fn() -> i32 {yield_number};
+ scope 1 (inlined inline_fn::<fn() -> i32 {yield_number}>) {
+ let mut _2: fn() -> i32 {yield_number};
+ let mut _3: ();
+ scope 2 (inlined <fn() -> i32 {yield_number} as FnOnce<()>>::call_once - shim(fn() -> i32 {yield_number})) {
+ scope 3 (inlined yield_number) {
+ }
+ }
}
- bb1: {
+ bb0: {
+ StorageLive(_1);
+ _1 = yield_number;
+ StorageLive(_2);
+ _2 = move _1;
+ StorageLive(_3);
+ _3 = ();
+ _0 = const 64_i32;
+ StorageDead(_3);
+ StorageDead(_2);
+ StorageDead(_1);
return;
}
}

View file

@ -0,0 +1,68 @@
// MIR for `num_to_digit` after PreCodegen
fn num_to_digit(_1: char) -> u32 {
debug num => _1;
let mut _0: u32;
let mut _4: std::option::Option<u32>;
scope 1 (inlined char::methods::<impl char>::is_digit) {
let _2: std::option::Option<u32>;
scope 2 (inlined Option::<u32>::is_some) {
let mut _3: isize;
}
}
scope 3 (inlined #[track_caller] Option::<u32>::unwrap) {
let mut _5: isize;
let mut _6: !;
scope 4 {
}
}
bb0: {
StorageLive(_2);
_2 = char::methods::<impl char>::to_digit(copy _1, const 8_u32) -> [return: bb1, unwind unreachable];
}
bb1: {
StorageLive(_3);
_3 = discriminant(_2);
StorageDead(_2);
switchInt(move _3) -> [1: bb2, otherwise: bb7];
}
bb2: {
StorageDead(_3);
StorageLive(_4);
_4 = char::methods::<impl char>::to_digit(move _1, const 8_u32) -> [return: bb3, unwind unreachable];
}
bb3: {
StorageLive(_5);
_5 = discriminant(_4);
switchInt(move _5) -> [0: bb4, 1: bb5, otherwise: bb6];
}
bb4: {
_6 = option::unwrap_failed() -> unwind unreachable;
}
bb5: {
_0 = move ((_4 as Some).0: u32);
StorageDead(_5);
StorageDead(_4);
goto -> bb8;
}
bb6: {
unreachable;
}
bb7: {
StorageDead(_3);
_0 = const 0_u32;
goto -> bb8;
}
bb8: {
return;
}
}

View file

@ -0,0 +1,68 @@
// MIR for `num_to_digit` after PreCodegen
fn num_to_digit(_1: char) -> u32 {
debug num => _1;
let mut _0: u32;
let mut _4: std::option::Option<u32>;
scope 1 (inlined char::methods::<impl char>::is_digit) {
let _2: std::option::Option<u32>;
scope 2 (inlined Option::<u32>::is_some) {
let mut _3: isize;
}
}
scope 3 (inlined #[track_caller] Option::<u32>::unwrap) {
let mut _5: isize;
let mut _6: !;
scope 4 {
}
}
bb0: {
StorageLive(_2);
_2 = char::methods::<impl char>::to_digit(copy _1, const 8_u32) -> [return: bb1, unwind continue];
}
bb1: {
StorageLive(_3);
_3 = discriminant(_2);
StorageDead(_2);
switchInt(move _3) -> [1: bb2, otherwise: bb7];
}
bb2: {
StorageDead(_3);
StorageLive(_4);
_4 = char::methods::<impl char>::to_digit(move _1, const 8_u32) -> [return: bb3, unwind continue];
}
bb3: {
StorageLive(_5);
_5 = discriminant(_4);
switchInt(move _5) -> [0: bb4, 1: bb5, otherwise: bb6];
}
bb4: {
_6 = option::unwrap_failed() -> unwind continue;
}
bb5: {
_0 = move ((_4 as Some).0: u32);
StorageDead(_5);
StorageDead(_4);
goto -> bb8;
}
bb6: {
unreachable;
}
bb7: {
StorageDead(_3);
_0 = const 0_u32;
goto -> bb8;
}
bb8: {
return;
}
}

View file

@ -1,4 +1,6 @@
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
// EMIT_MIR_FOR_EACH_BIT_WIDTH
// This test is a mirror of codegen/issue-59352.rs. // This test is a mirror of codegen/issue-59352.rs.
// The LLVM inliner doesn't inline `char::method::is_digit()` and so it doesn't recognize this case // The LLVM inliner doesn't inline `char::method::is_digit()` and so it doesn't recognize this case
// as effectively `if x.is_some() { x.unwrap() } else { 0 }`. // as effectively `if x.is_some() { x.unwrap() } else { 0 }`.

View file

@ -29,6 +29,10 @@ fn mapped(_1: impl Iterator<Item = T>, _2: impl Fn(T) -> U) -> () {
let mut _12: U; let mut _12: U;
scope 6 { scope 6 {
debug x => _10; debug x => _10;
scope 7 (inlined ops::function::impls::<impl FnOnce<(T,)> for &mut impl Fn(T) -> U>::call_once) {
debug self => _8;
debug args => _11;
}
} }
} }
} }
@ -85,7 +89,7 @@ fn mapped(_1: impl Iterator<Item = T>, _2: impl Fn(T) -> U) -> () {
StorageLive(_12); StorageLive(_12);
StorageLive(_11); StorageLive(_11);
_11 = (copy _10,); _11 = (copy _10,);
_12 = <&mut impl Fn(T) -> U as FnOnce<(T,)>>::call_once(move _8, move _11) -> [return: bb7, unwind: bb10]; _12 = <impl Fn(T) -> U as FnMut<(T,)>>::call_mut(move _8, move _11) -> [return: bb7, unwind: bb10];
} }
bb7: { bb7: {