From ab06e2749d931efdd014c346f9a1caa33029711c Mon Sep 17 00:00:00 2001 From: Makro Date: Sat, 1 Aug 2026 10:02:44 +0000 Subject: [PATCH] perf: mbe: compile rule RHS into a pre-flattened transcription template Transcription walked the RHS tree token by token on every expansion: one dispatch, span mark, and buffer push per literal token, plus a metavar normalization per occurrence. The tree is now lowered once per rule (lazily, on first expansion) into a template whose runs of literal tokens, including whole literal delimited groups, are pre-encoded as flat entries. Transcription copies each run into the output buffer, rebases depths and match indices, and marks the copied spans in place. Metavar occurrences carry their normalization precomputed, and the sequence lockstep check walks a precollected list of the metavars in the sequence subtree instead of re-walking the tree. clap_derive check -0.52%, html5ever check -0.46%, syn check -0.27%, tt-muncher check -1.81%, no regressions. --- compiler/rustc_expand/src/mbe/macro_rules.rs | 32 +- compiler/rustc_expand/src/mbe/metavar_expr.rs | 4 +- compiler/rustc_expand/src/mbe/transcribe.rs | 468 ++++++++++++------ 3 files changed, 327 insertions(+), 177 deletions(-) diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index e0d7c780c14b6..ef0404d0a8fbe 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -42,7 +42,7 @@ use crate::expand::{AstFragment, AstFragmentKind, ensure_complete_parse, parse_a use crate::mbe::macro_check::check_meta_variables; use crate::mbe::macro_parser::{Error, ErrorReported, Failure, MatcherLoc, Success, TtParser}; use crate::mbe::quoted::{RulePart, parse_one_tt}; -use crate::mbe::transcribe::transcribe; +use crate::mbe::transcribe::{MacroRhs, transcribe}; use crate::mbe::{self, KleeneOp}; pub(crate) struct ParserAnyMacro<'a, 'b> { @@ -148,7 +148,7 @@ impl<'a, 'b> ParserAnyMacro<'a, 'b> { pub(crate) enum MacroRule { /// A function-style rule, for use with `m!()` - Func { lhs: Vec, lhs_span: Span, rhs: mbe::TokenTree }, + Func { lhs: Vec, lhs_span: Span, rhs: MacroRhs }, /// An attr rule, for use with `#[m]` Attr { unsafe_rule: bool, @@ -156,10 +156,10 @@ pub(crate) enum MacroRule { args_span: Span, body: Vec, body_span: Span, - rhs: mbe::TokenTree, + rhs: MacroRhs, }, /// A derive rule, for use with `#[m]` - Derive { body: Vec, body_span: Span, rhs: mbe::TokenTree }, + Derive { body: Vec, body_span: Span, rhs: MacroRhs }, } pub struct MacroRulesMacroExpander { @@ -183,7 +183,7 @@ impl MacroRulesMacroExpander { } MacroRule::Derive { body_span, ref rhs, .. } => (MultiSpan::from_span(body_span), rhs), }; - if has_compile_error_macro(rhs) { None } else { Some((&self.name, span)) } + if has_compile_error_macro(&rhs.tt) { None } else { Some((&self.name, span)) } } pub fn kinds(&self) -> MacroKinds { @@ -220,12 +220,9 @@ impl MacroRulesMacroExpander { let MacroRule::Derive { rhs, .. } = rule else { panic!("try_match_macro_derive returned non-derive rule"); }; - let mbe::TokenTree::Delimited(rhs_span, _, rhs) = rhs else { - cx.dcx().span_bug(sp, "malformed macro derive rhs"); - }; let id = cx.current_expansion.id; - let tts = transcribe(psess, &named_matches, rhs, *rhs_span, self.transparency, id) + let tts = transcribe(psess, &named_matches, rhs, self.transparency, id) .map_err(|e| e.emit())? .to_token_stream(); @@ -399,14 +396,11 @@ fn expand_macro<'cx, 'a: 'cx>( let MacroRule::Func { lhs, rhs, .. } = rule else { panic!("try_match_macro returned non-func rule"); }; - let mbe::TokenTree::Delimited(rhs_span, _, rhs) = rhs else { - cx.dcx().span_bug(sp, "malformed macro rhs"); - }; - let arm_span = rhs_span.entire(); + let arm_span = rhs.tt.span(); // rhs has holes ( `$id` and `$(...)` that need filled) let id = cx.current_expansion.id; - let flat = match transcribe(psess, &named_matches, rhs, *rhs_span, transparency, id) { + let flat = match transcribe(psess, &named_matches, rhs, transparency, id) { Ok(flat) => flat, Err(err) => { let guar = err.emit(); @@ -484,9 +478,6 @@ fn expand_macro_attr( let MacroRule::Attr { rhs, unsafe_rule, .. } = rule else { panic!("try_macro_match_attr returned non-attr rule"); }; - let mbe::TokenTree::Delimited(rhs_span, _, rhs) = rhs else { - cx.dcx().span_bug(sp, "malformed macro rhs"); - }; match (safety, unsafe_rule) { (Safety::Default, false) | (Safety::Unsafe(_), true) => {} @@ -502,7 +493,7 @@ fn expand_macro_attr( } let id = cx.current_expansion.id; - let tts = transcribe(psess, &named_matches, rhs, *rhs_span, transparency, id) + let tts = transcribe(psess, &named_matches, rhs, transparency, id) .map_err(|e| e.emit())? .to_token_stream(); @@ -838,11 +829,12 @@ pub fn compile_declarative_macro( }; let args = mbe::macro_parser::compute_locs(&delimited.tts); let body_span = lhs_span; + let rhs = MacroRhs::new(rhs); rules.push(MacroRule::Attr { unsafe_rule, args, args_span, body: lhs, body_span, rhs }); } else if is_derive { - rules.push(MacroRule::Derive { body: lhs, body_span: lhs_span, rhs }); + rules.push(MacroRule::Derive { body: lhs, body_span: lhs_span, rhs: MacroRhs::new(rhs) }); } else { - rules.push(MacroRule::Func { lhs, lhs_span, rhs }); + rules.push(MacroRule::Func { lhs, lhs_span, rhs: MacroRhs::new(rhs) }); } if p.token == token::Eof { break; diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index a02b84204cb39..3d399ba2fb0b5 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -13,7 +13,7 @@ pub(crate) const RAW_IDENT_ERR: &str = "`${concat(..)}` currently does not suppo pub(crate) const UNSUPPORTED_CONCAT_ELEM_ERR: &str = "expected identifier or string literal"; /// A meta-variable expression, for expansions based on properties of meta-variables. -#[derive(Debug, PartialEq, Encodable, Decodable)] +#[derive(Clone, Debug, PartialEq, Encodable, Decodable)] pub(crate) enum MetaVarExpr { /// Unification of two or more identifiers. Concat(Box<[MetaVarExprConcatElem]>), @@ -157,7 +157,7 @@ fn iter_span(iter: &TokenStreamIter<'_>) -> Option { /// Indicates what is placed in a `concat` parameter. For example, literals /// (`${concat("foo", "bar")}`) or adhoc identifiers (`${concat(foo, bar)}`). -#[derive(Debug, Decodable, Encodable, PartialEq)] +#[derive(Clone, Debug, Decodable, Encodable, PartialEq)] pub(crate) enum MetaVarExprConcatElem { /// Identifier WITHOUT a preceding dollar sign, which means that this identifier should be /// interpreted as a literal. diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index f7ed65de9a488..a8a49dff88dcb 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -1,9 +1,13 @@ +use std::mem; +use std::sync::OnceLock; + use rustc_ast::token::{ self, Delimiter, IdentIsRaw, InvisibleOrigin, Lit, LitKind, MetaVarKind, Token, TokenKind, }; use rustc_ast::tokenstream::{ - DelimSpacing, DelimSpan, FlatSink, FlatTokenCursor, FlatTt, Spacing, TokenStream, TokenTree, + DelimSpacing, DelimSpan, FlatEntry, FlatSink, FlatTokenCursor, FlatTt, Spacing, TokenStream, + TokenTree, }; use rustc_ast::{ExprKind, StmtKind, TyKind, UnOp}; use rustc_data_structures::fx::FxHashMap; @@ -41,10 +45,10 @@ struct TranscrCtx<'psess, 'itp> { /// The stack of things yet to be completely expanded. /// - /// We descend into the RHS (`src`), expanding things as we go. This stack contains the things - /// we have yet to expand/are still expanding. We start the stack off with the whole RHS. The - /// choice of spacing values doesn't matter. - stack: SmallVec<[Frame<'itp>; 1]>, + /// We descend into the compiled template of the RHS, expanding things as + /// we go. This stack contains the things we have yet to expand/are still + /// expanding. We start the stack off with the whole template. + stack: SmallVec<[ExecFrame<'itp>; 1]>, /// A stack of where we are in the repeat expansion. /// @@ -100,44 +104,229 @@ impl Marker { } } -/// An iterator over the token trees in a delimited token tree (`{ ... }`) or a sequence (`$(...)`). -struct Frame<'a> { - tts: &'a [mbe::TokenTree], - idx: usize, - kind: FrameKind, +/// A macro rule RHS: the parsed tree plus its transcription template, +/// compiled on first expansion so definitions that are never invoked don't +/// pay for compilation. +pub(crate) struct MacroRhs { + pub(crate) tt: mbe::TokenTree, + template: OnceLock