Skip to content

Commit e233072

Browse files
committed
Optimize syntax context allocation performance in macro hygiene system
Signed-off-by: xizheyin <[email protected]>
1 parent 47a40b2 commit e233072

File tree

1 file changed

+39
-17
lines changed

1 file changed

+39
-17
lines changed

compiler/rustc_span/src/hygiene.rs

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -578,35 +578,32 @@ impl HygieneData {
578578
}
579579

580580
// Reserve a new syntax context.
581-
// The inserted dummy data can only be potentially accessed by nested `alloc_ctxt` calls,
582-
// the assert below ensures that it doesn't happen.
583581
let ctxt = SyntaxContext::from_usize(self.syntax_context_data.len());
584582
self.syntax_context_data
585583
.push(SyntaxContextData { dollar_crate_name: sym::dummy, ..SyntaxContextData::root() });
586584
self.syntax_context_map.insert(key, ctxt);
587585

588-
// Opaque and semi-opaque versions of the parent. Note that they may be equal to the
589-
// parent itself. E.g. `parent_opaque` == `parent` if the expn chain contains only opaques,
590-
// and `parent_opaque_and_semiopaque` == `parent` if the expn contains only (semi-)opaques.
586+
// Get parent data
591587
let parent_data = &self.syntax_context_data[parent.0 as usize];
592588
assert_ne!(parent_data.dollar_crate_name, sym::dummy);
593589
let parent_opaque = parent_data.opaque;
594590
let parent_opaque_and_semiopaque = parent_data.opaque_and_semiopaque;
595591

596-
// Evaluate opaque and semi-opaque versions of the new syntax context.
592+
// Evaluate opaque and semi-opaque versions iteratively to avoid recursive calls
597593
let (opaque, opaque_and_semiopaque) = match transparency {
598594
Transparency::Transparent => (parent_opaque, parent_opaque_and_semiopaque),
599-
Transparency::SemiOpaque => (
600-
parent_opaque,
601-
// Will be the same as `ctxt` if the expn chain contains only (semi-)opaques.
602-
self.alloc_ctxt(parent_opaque_and_semiopaque, expn_id, transparency),
603-
),
604-
Transparency::Opaque => (
605-
// Will be the same as `ctxt` if the expn chain contains only opaques.
606-
self.alloc_ctxt(parent_opaque, expn_id, transparency),
607-
// Will be the same as `ctxt` if the expn chain contains only (semi-)opaques.
608-
self.alloc_ctxt(parent_opaque_and_semiopaque, expn_id, transparency),
609-
),
595+
Transparency::SemiOpaque => {
596+
let opaque = parent_opaque;
597+
let semi_opaque =
598+
self.alloc_ctxt_if_needed(parent_opaque_and_semiopaque, expn_id, transparency);
599+
(opaque, semi_opaque)
600+
}
601+
Transparency::Opaque => {
602+
let opaque = self.alloc_ctxt_if_needed(parent_opaque, expn_id, transparency);
603+
let semi_opaque =
604+
self.alloc_ctxt_if_needed(parent_opaque_and_semiopaque, expn_id, transparency);
605+
(opaque, semi_opaque)
606+
}
610607
};
611608

612609
// Fill the full data, now that we have it.
@@ -620,6 +617,31 @@ impl HygieneData {
620617
};
621618
ctxt
622619
}
620+
621+
/// Helper to allocate context only if it would be different from the current one.
622+
/// This avoids unnecessary recursive calls in common cases.
623+
#[inline]
624+
fn alloc_ctxt_if_needed(
625+
&mut self,
626+
parent: SyntaxContext,
627+
expn_id: ExpnId,
628+
transparency: Transparency,
629+
) -> SyntaxContext {
630+
// Fast path: if parent would be the same, return current context being built
631+
let current_idx = self.syntax_context_data.len() - 1;
632+
if parent.0 as usize == current_idx {
633+
return SyntaxContext::from_usize(current_idx);
634+
}
635+
636+
// Check cache first
637+
let key = (parent, expn_id, transparency);
638+
if let Some(ctxt) = self.syntax_context_map.get(&key) {
639+
return *ctxt;
640+
}
641+
642+
// Need to compute
643+
self.alloc_ctxt(parent, expn_id, transparency)
644+
}
623645
}
624646

625647
pub fn walk_chain(span: Span, to: SyntaxContext) -> Span {

0 commit comments

Comments
 (0)