Skip to content

Commit b1de36f

Browse files
committed
Auto merge of rust-lang#127421 - cjgillot:cache-iter, r=fmease
Cache hir_owner_nodes in ParentHirIterator. Lint level computation may traverse deep HIR trees using that iterator. This calls `hir_owner_nodes` many times for the same HIR owner, which is wasterful. This PR caches the value to allow a more efficient iteration scheme. r? ghost for perf
2 parents 89aefb9 + 0184c6f commit b1de36f

File tree

1 file changed

+26
-8
lines changed
  • compiler/rustc_middle/src/hir/map

1 file changed

+26
-8
lines changed

compiler/rustc_middle/src/hir/map/mod.rs

+26-8
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,18 @@ pub struct Map<'hir> {
3131

3232
/// An iterator that walks up the ancestor tree of a given `HirId`.
3333
/// Constructed using `tcx.hir().parent_iter(hir_id)`.
34-
pub struct ParentHirIterator<'hir> {
34+
struct ParentHirIterator<'hir> {
3535
current_id: HirId,
3636
map: Map<'hir>,
37+
// Cache the current value of `hir_owner_nodes` to avoid repeatedly calling the same query for
38+
// the same owner, which will uselessly record many times the same query dependency.
39+
current_owner_nodes: Option<&'hir OwnerNodes<'hir>>,
40+
}
41+
42+
impl<'hir> ParentHirIterator<'hir> {
43+
fn new(map: Map<'hir>, current_id: HirId) -> ParentHirIterator<'hir> {
44+
ParentHirIterator { current_id, map, current_owner_nodes: None }
45+
}
3746
}
3847

3948
impl<'hir> Iterator for ParentHirIterator<'hir> {
@@ -44,13 +53,22 @@ impl<'hir> Iterator for ParentHirIterator<'hir> {
4453
return None;
4554
}
4655

47-
// There are nodes that do not have entries, so we need to skip them.
48-
let parent_id = self.map.tcx.parent_hir_id(self.current_id);
56+
let HirId { owner, local_id } = self.current_id;
4957

50-
if parent_id == self.current_id {
51-
self.current_id = CRATE_HIR_ID;
52-
return None;
53-
}
58+
let parent_id = if local_id == ItemLocalId::ZERO {
59+
// We go from an owner to its parent, so clear the cache.
60+
self.current_owner_nodes = None;
61+
self.map.tcx.hir_owner_parent(owner)
62+
} else {
63+
let owner_nodes =
64+
self.current_owner_nodes.get_or_insert_with(|| self.map.tcx.hir_owner_nodes(owner));
65+
let parent_local_id = owner_nodes.nodes[local_id].parent;
66+
// HIR indexing should have checked that.
67+
debug_assert_ne!(parent_local_id, local_id);
68+
HirId { owner, local_id: parent_local_id }
69+
};
70+
71+
debug_assert_ne!(parent_id, self.current_id);
5472

5573
self.current_id = parent_id;
5674
return Some(parent_id);
@@ -479,7 +497,7 @@ impl<'hir> Map<'hir> {
479497
/// until the crate root is reached. Prefer this over your own loop using `parent_id`.
480498
#[inline]
481499
pub fn parent_id_iter(self, current_id: HirId) -> impl Iterator<Item = HirId> + 'hir {
482-
ParentHirIterator { current_id, map: self }
500+
ParentHirIterator::new(self, current_id)
483501
}
484502

485503
/// Returns an iterator for the nodes in the ancestor tree of the `current_id`

0 commit comments

Comments
 (0)