Fuse byte strings and inline grammar rules in place - #729
Merged
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR refactors the ByteStringFuser and RuleInliner grammar-optimization passes to avoid rebuilding the full grammar on every invocation, switching them to in-place rewrites (static void Apply(Grammar* grammar)) with copy-on-first-rewrite behavior to preserve external holders of the original grammar.
Changes:
- Convert
ByteStringFuserandRuleInlinerfrom “return-new-grammar” passes to in-place passes, and updateGrammarOptimizerto call the new APIs. - Add in-place mutation helpers on
Grammar::Implto append new expr records and shrink existing records’ payloads. - Update TVM FFI testing bindings to the new in-place pass signatures.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| cpp/tvm_ffi/tvm_ffi.cc | Updates test bindings to call the new in-place optimizer passes. |
| cpp/grammar_impl.h | Adds AddGrammarExpr and ShrinkGrammarExprData to support in-place record updates. |
| cpp/grammar_functor.h | Updates pass API signatures and clarifies in-place/copy-on-write behavior in docs. |
| cpp/grammar_functor.cc | Implements in-place byte-string fusing and rule inlining, and updates optimizer flow. |
Comments suppressed due to low confidence (1)
cpp/grammar_functor.cc:2140
- Same as in
RuleInlinerImpl: this deep-copy step clonesoptimizedand precomputed FSM caches (complete_fsm,per_rule_fsms, etc.) from the sourceGrammar::Impl(viaGrammarBuilder(const Grammar&)). Because the pass then mutates expr records in-place, those caches become stale; ifByteStringFuser::Applyis called on an optimized grammar, the resulting grammar may still appear optimized while its FSMs no longer match the grammar.
if (!owned) {
// Copy before the first rewrite so that other holders of this grammar are unaffected.
grammar_ref = GrammarBuilder(grammar_ref).Get(grammar_ref->GetRootRuleId());
owned = true;
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
606
to
610
| if (!owned) { | ||
| // Copy before the first rewrite so that other holders of this grammar are unaffected. | ||
| grammar_ref = GrammarBuilder(grammar_ref).Get(grammar_ref->GetRootRuleId()); | ||
| owned = true; | ||
| } |
This was referenced Jul 27, 2026
Ubospica
force-pushed
the
perf/in-place-optimizer-passes
branch
from
July 28, 2026 10:33
9363783 to
647c4d0
Compare
Cover fusing shapes (full run, split runs, no-op), non-inlinable rules (empty-string choice, rule-ref body, mid-sequence ref), lookahead assertion rewriting/preservation, and input grammar immutability.
FromMutableGrammar no longer builds the rule name map (name-based operations are not needed by in-place passes), the deep copies at the optimizer entry and in the FFI wrappers copy the impl directly instead of going through GrammarBuilder, the expr memo and the inlinability cache use plain arrays indexed by dense ids instead of hash maps, and unchanged sequences/choices are probed read-only without materializing child id vectors.
Builders bound to an existing grammar (copy constructor and FromMutableGrammar) defer building the rule name map to the first name-based operation (AddRule, GetNewRuleName, GetRuleId), so id-only rewriting pays no name hashing cost while name-based operations keep working on any builder.
Remove empty byte strings before nullable analysis and recursively visit nested expressions during rule inlining.
Ubospica
force-pushed
the
perf/in-place-optimizer-passes
branch
from
July 28, 2026 12:50
647c4d0 to
bc65e27
Compare
This was referenced Jul 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
改动
语法优化器中的字节串合并会把相邻字面字符串合成一段,规则内联会用规则内容替换引用。旧实现每次执行都重建整份语法树,包括未改变的子树。本请求把两个阶段改为单次遍历的原地重写:
同时修正两个正确性问题:从序列中删除空字节串,保持空规则分析正确;规则内联时递归访问新复制的嵌套表达式与序列剩余元素。
性能
使用发布构建,每个版本交替运行两组,每组 15 个样本,下列结果为 30 个样本的中位数。
单独优化阶段:
完整流程:
验证