Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only check the DefId for the recursion check in MIR inliner. #100522

Merged
merged 1 commit into from
Aug 19, 2022

Conversation

cjgillot
Copy link
Contributor

@cjgillot cjgillot commented Aug 14, 2022

The current history check compares Instances, so it cannot detect cases of polymorphic recursion where Substs change.
This PR makes it so we only compare DefIds, ignoring any change in Substs.

According to #100522 (comment), in practice only very few inlining decisions change.

Fixes #100476

@rust-highfive
Copy link
Collaborator

r? @lcnr

(rust-highfive has picked a reviewer for you, use r? to override)

@rustbot rustbot added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Aug 14, 2022
@rustbot
Copy link
Collaborator

rustbot commented Aug 14, 2022

Some changes occurred to MIR optimizations

cc @rust-lang/wg-mir-opt

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Aug 14, 2022
@tmiasko
Copy link
Contributor

tmiasko commented Aug 14, 2022

Waiting until a depth limit is hit is not enough to avoid the issue in a variations of the original code, e.g., with two self-recursive calls instead of just one.

Maybe keep a history with def ids only? That should be quite robust. In fact there used to be a self-recursion check based on def id only, but looks like it is gone now?. While in the principle there are cases with recursive def ids which we would be good candidates for inlining, they are relatively uncommon in practice. While bootstraping rustc, a recursion check that uses def ids only differs from the one that also includes substitutions only 26 times.

@lcnr
Copy link
Contributor

lcnr commented Aug 15, 2022

r? @tmiasko looks like you have thoughts on how this should be handled

@rust-highfive rust-highfive assigned tmiasko and unassigned lcnr Aug 15, 2022
@cjgillot
Copy link
Contributor Author

Waiting until a depth limit is hit is not enough to avoid the issue in a variations of the original code, e.g., with two self-recursive calls instead of just one.

Why? With two self-recursive calls, each inlined call would increase the length of history, wouldn't it? It works as expected on:

fn inner<T>() { inner2<T::Next>() }
fn inner2<T>() { inner<T::Next>() }

Maybe keep a history with def ids only? That should be quite robust. In fact there used to be a self-recursion check based on def id only, but looks like it is gone now?. While in the principle there are cases with recursive def ids which we would be good candidates for inlining, they are relatively uncommon in practice. While bootstraping rustc, a recursion check that uses def ids only differs from the one that also includes substitutions only 26 times.

I could only find you PR which introduced the check based on Instance 😉. I guess we can.
Do you have the list of those 26 times? This would tell which case we are regressing.

@tmiasko
Copy link
Contributor

tmiasko commented Aug 15, 2022

With two self-recursive calls, each inlined call would increase the length of history, wouldn't it?

I had in mind a case with two calls, where each call site has an independent history, growing in both breadth and depth:

        #[inline(always)]
        fn inner<const N: usize, T: Tr>() {
            inner::<N, T::Next>();
            inner::<N, T::Next>();
        }

I could only find you PR which introduced the check based on Instance 😉.

The check based on def id was removed in 114c928 (it prevented inlining of functions with direct self-recursion).

Do you have the list of those 26 times? This would tell which case we are regressing.

Differences: instance that is being optimized, callee
MirSource { instance: Item(WithOptConstParam { did: DefId(0:46730 ~ core[ef75]::iter::adapters::peekable::{impl#8}::clone), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(0:9068 ~ core[ef75]::option::{impl#5}::clone), const_param_did: None }), substs: [<I as iter::traits::iterator::Iterator>::Item] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:93 ~ rustc_demangle[b6dd]::v0::{impl#3}::try_parse_str_chars::{closure#2}), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(2:6994 ~ core[ef75]::iter::adapters::map::{impl#2}::next), const_param_did: None }), substs: [[&u8; 2], rustc_std_workspace_core::slice::ChunksExact<u8>, [closure@~/.cargo/registry/src/github.com-1ecc6299db9ec823/rustc-demangle-0.1.21/src/v0.rs:303:18: 303:25]] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:94 ~ rustc_demangle[b6dd]::v0::{impl#3}::try_parse_str_chars::{closure#2}::{closure#0}), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(2:6994 ~ core[ef75]::iter::adapters::map::{impl#2}::next), const_param_did: None }), substs: [[&u8; 2], rustc_std_workspace_core::slice::ChunksExact<u8>, [closure@~/.cargo/registry/src/github.com-1ecc6299db9ec823/rustc-demangle-0.1.21/src/v0.rs:303:18: 303:25]] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:46730 ~ core[ef75]::iter::adapters::peekable::{impl#8}::clone), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(0:9068 ~ core[ef75]::option::{impl#5}::clone), const_param_did: None }), substs: [<I as iter::traits::iterator::Iterator>::Item] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:93 ~ rustc_demangle[b6dd]::v0::{impl#3}::try_parse_str_chars::{closure#2}), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(2:6994 ~ core[ef75]::iter::adapters::map::{impl#2}::next), const_param_did: None }), substs: [[&u8; 2], rustc_std_workspace_core::slice::ChunksExact<u8>, [closure@~/.cargo/registry/src/github.com-1ecc6299db9ec823/rustc-demangle-0.1.21/src/v0.rs:303:18: 303:25]] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:94 ~ rustc_demangle[b6dd]::v0::{impl#3}::try_parse_str_chars::{closure#2}::{closure#0}), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(2:6994 ~ core[ef75]::iter::adapters::map::{impl#2}::next), const_param_did: None }), substs: [[&u8; 2], rustc_std_workspace_core::slice::ChunksExact<u8>, [closure@~/.cargo/registry/src/github.com-1ecc6299db9ec823/rustc-demangle-0.1.21/src/v0.rs:303:18: 303:25]] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:93 ~ rustc_demangle[7ef0]::v0::{impl#3}::try_parse_str_chars::{closure#2}), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(1:6994 ~ core[ef75]::iter::adapters::map::{impl#2}::next), const_param_did: None }), substs: [[&u8; 2], core::slice::ChunksExact<u8>, [closure@~/.cargo/registry/src/github.com-1ecc6299db9ec823/rustc-demangle-0.1.21/src/v0.rs:303:18: 303:25]] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:94 ~ rustc_demangle[7ef0]::v0::{impl#3}::try_parse_str_chars::{closure#2}::{closure#0}), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(1:6994 ~ core[ef75]::iter::adapters::map::{impl#2}::next), const_param_did: None }), substs: [[&u8; 2], core::slice::ChunksExact<u8>, [closure@~/.cargo/registry/src/github.com-1ecc6299db9ec823/rustc-demangle-0.1.21/src/v0.rs:303:18: 303:25]] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:344 ~ cc[5fa3]::{impl#9}::clone), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(2:9068 ~ core[ef75]::option::{impl#5}::clone), const_param_did: None }), substs: [std::string::String] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:219 ~ cc[5fa3]::{impl#6}::get_cpp_link_stdlib), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(2:9068 ~ core[ef75]::option::{impl#5}::clone), const_param_did: None }), substs: [std::string::String] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:398 ~ tracing_core[54ca]::field::{impl#35}::field::{closure#0}), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(2:3041 ~ core[ef75]::cmp::impls::{impl#9}::eq), const_param_did: None }), substs: [ReErased, ReErased, str, str] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:3564 ~ rustc_middle[f071]::mir::pretty::write_allocations::{closure#0}), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(2:7008 ~ core[ef75]::iter::adapters::map::{impl#3}::next_back), const_param_did: None }), substs: [&mir::interpret::AllocId, std::slice::Iter<(rustc_target::abi::Size, mir::interpret::AllocId)>, [closure@rustc_data_structures::sorted_map::SortedMap<rustc_target::abi::Size, mir::interpret::AllocId>::values::{closure#0}]] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:7210 ~ rustc_middle[f071]::ty::layout::{impl#3}::layout_of_uncached::{closure#16}), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(2:6994 ~ core[ef75]::iter::adapters::map::{impl#2}::next), const_param_did: None }), substs: [usize, std::ops::Range<usize>, [closure@rustc_target::abi::FieldsShape::index_by_increasing_offset::{closure#0}]] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:7190 ~ rustc_middle[f071]::ty::layout::{impl#3}::layout_of_uncached), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(2:6994 ~ core[ef75]::iter::adapters::map::{impl#2}::next), const_param_did: None }), substs: [(rustc_target::abi::VariantIdx, &ty::VariantDef), std::iter::Enumerate<std::slice::Iter<ty::VariantDef>>, [closure@rustc_index::vec::IndexVec<rustc_target::abi::VariantIdx, ty::VariantDef>::iter_enumerated::{closure#0}]] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:275 ~ rustc_mir_dataflow[2f90]::elaborate_drops::{impl#2}::open_drop_for_multivariant), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(2:6994 ~ core[ef75]::iter::adapters::map::{impl#2}::next), const_param_did: None }), substs: [(rustc_target::abi::VariantIdx, &rustc_middle::ty::VariantDef), std::iter::Enumerate<std::slice::Iter<rustc_middle::ty::VariantDef>>, [closure@rustc_index::vec::IndexVec<rustc_target::abi::VariantIdx, rustc_middle::ty::VariantDef>::iter_enumerated::{closure#0}]] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:1669 ~ rustc_metadata[11fd]::rmeta::encoder::{impl#11}::encode_def_path_table), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(2:6994 ~ core[ef75]::iter::adapters::map::{impl#2}::next), const_param_did: None }), substs: [(rustc_span::def_id::DefIndex, &rustc_hir::definitions::DefKey), std::iter::Enumerate<std::slice::Iter<rustc_hir::definitions::DefKey>>, [closure@rustc_index::vec::IndexVec<rustc_span::def_id::DefIndex, rustc_hir::definitions::DefKey>::iter_enumerated::{closure#0}]] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:1691 ~ rustc_metadata[11fd]::rmeta::encoder::{impl#12}::encode_def_ids), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(2:6994 ~ core[ef75]::iter::adapters::map::{impl#2}::next), const_param_did: None }), substs: [rustc_span::def_id::DefIndex, std::ops::Range<usize>, [closure@rustc_index::vec::IndexVec<rustc_span::def_id::DefIndex, rustc_span::def_id::DefPathHash>::indices::{closure#0}]] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:2314 ~ rustc_infer[52fb]::infer::error_reporting::{impl#0}::cmp::{closure#0}), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(2:3041 ~ core[ef75]::cmp::impls::{impl#9}::eq), const_param_did: None }), substs: [ReErased, ReErased, rustc_middle::ty::Ty, rustc_middle::ty::Ty] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:2317 ~ rustc_infer[52fb]::infer::error_reporting::{impl#0}::cmp::{closure#1}), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(2:3041 ~ core[ef75]::cmp::impls::{impl#9}::eq), const_param_did: None }), substs: [ReErased, ReErased, str, str] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:1302 ~ rustc_resolve[0ff7]::diagnostics::show_candidates::{closure#8}), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(2:3041 ~ core[ef75]::cmp::impls::{impl#9}::eq), const_param_did: None }), substs: [ReErased, ReErased, str, str] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:135 ~ rustc_transmute[9aba]::layout::tree::rustc::{impl#3}::from_ty), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(2:6994 ~ core[ef75]::iter::adapters::map::{impl#2}::next), const_param_did: None }), substs: [(rustc_target::abi::VariantIdx, &rustc_middle::ty::VariantDef), std::iter::Enumerate<std::slice::Iter<rustc_middle::ty::VariantDef>>, [closure@rustc_index::vec::IndexVec<rustc_target::abi::VariantIdx, rustc_middle::ty::VariantDef>::iter_enumerated::{closure#0}]] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:13668 ~ rustc_borrowck[bbba]::do_mir_borrowck), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(2:6994 ~ core[ef75]::iter::adapters::map::{impl#2}::next), const_param_did: None }), substs: [(rustc_middle::mir::Promoted, &rustc_middle::mir::Body), std::iter::Enumerate<std::slice::Iter<rustc_middle::mir::Body>>, [closure@rustc_index::vec::IndexVec<rustc_middle::mir::Promoted, rustc_middle::mir::Body>::iter_enumerated::{closure#0}]] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:7570 ~ rustc_typeck[4ccc]::coherence::inherent_impls_overlap::{impl#0}::impls_have_common_items), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(2:6994 ~ core[ef75]::iter::adapters::map::{impl#2}::next), const_param_did: None }), substs: [(&rustc_span::Symbol, &&rustc_middle::ty::AssocItem), std::slice::Iter<(rustc_span::Symbol, &rustc_middle::ty::AssocItem)>, [closure@rustc_data_structures::sorted_map::SortedIndexMultiMap<u32, rustc_span::Symbol, &rustc_middle::ty::AssocItem>::iter::{closure#0}]] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:7573 ~ rustc_typeck[4ccc]::coherence::inherent_impls_overlap::{impl#0}::check_for_common_items_in_impls), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(2:6994 ~ core[ef75]::iter::adapters::map::{impl#2}::next), const_param_did: None }), substs: [(&rustc_span::Symbol, &&rustc_middle::ty::AssocItem), std::slice::Iter<(rustc_span::Symbol, &rustc_middle::ty::AssocItem)>, [closure@rustc_data_structures::sorted_map::SortedIndexMultiMap<u32, rustc_span::Symbol, &rustc_middle::ty::AssocItem>::iter::{closure#0}]] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:3085 ~ rustc_borrowck[bbba]::nll::populate_polonius_move_facts), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(2:6994 ~ core[ef75]::iter::adapters::map::{impl#2}::next), const_param_did: None }), substs: [(rustc_middle::mir::Local, &rustc_mir_dataflow::move_paths::MovePathIndex), std::iter::Enumerate<std::slice::Iter<rustc_mir_dataflow::move_paths::MovePathIndex>>, [closure@rustc_index::vec::IndexVec<rustc_middle::mir::Local, rustc_mir_dataflow::move_paths::MovePathIndex>::iter_enumerated::{closure#0}]] }
MirSource { instance: Item(WithOptConstParam { did: DefId(0:398 ~ tracing_core[5d90]::field::{impl#35}::field::{closure#0}), const_param_did: None }), promoted: None } Instance { def: Item(WithOptConstParam { did: DefId(2:3041 ~ core[ef75]::cmp::impls::{impl#9}::eq), const_param_did: None }), substs: [ReErased, ReErased, str, str] }

@cjgillot cjgillot force-pushed the inline-polymorphic-recursion branch from 584313a to 1cf8f21 Compare August 15, 2022 09:13
@cjgillot cjgillot force-pushed the inline-polymorphic-recursion branch from 1cf8f21 to 86645c9 Compare August 17, 2022 17:25
@tmiasko
Copy link
Contributor

tmiasko commented Aug 17, 2022

Thanks!

r=me with pull request description updated.

@cjgillot cjgillot changed the title Limit recursion depth in inliner. Only check the DefId for the recursion check in MIR inliner. Aug 17, 2022
@cjgillot
Copy link
Contributor Author

@bors r=tmiasko

@bors
Copy link
Contributor

bors commented Aug 17, 2022

📌 Commit 86645c9 has been approved by tmiasko

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Aug 17, 2022
bors added a commit to rust-lang-ci/rust that referenced this pull request Aug 19, 2022
Rollup of 9 pull requests

Successful merges:

 - rust-lang#99576 (Do not allow `Drop` impl on foreign fundamental types)
 - rust-lang#100081 (never consider unsafe blocks unused if they would be required with deny(unsafe_op_in_unsafe_fn))
 - rust-lang#100208 (make NOP dyn casts not require anything about the vtable)
 - rust-lang#100494 (Cleanup rustdoc themes)
 - rust-lang#100522 (Only check the `DefId` for the recursion check in MIR inliner.)
 - rust-lang#100592 (Manually implement Debug for ImportKind.)
 - rust-lang#100598 (Don't fix builtin index when Where clause is found)
 - rust-lang#100721 (Add diagnostics lints to `rustc_type_ir` module)
 - rust-lang#100731 (rustdoc: count deref and non-deref as same set of used methods)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit d83abe8 into rust-lang:master Aug 19, 2022
@rustbot rustbot added this to the 1.65.0 milestone Aug 19, 2022
@cjgillot cjgillot deleted the inline-polymorphic-recursion branch August 20, 2022 07:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

rustc hangs on a recursive function marked with #[inline]
6 participants