• 周日. 9 月 13th, 2026

编译器首次给多核词法分析‘上锁’:并行分词真的会泄露语义

词法分析器的并行分片在代码矩阵上以青光与琥珀光交错,象征分词扫描点在多核架构下被精确切分。

先把一个冷门名字摆出来——table-driven DFA 词法分析器。它是 clang、V8、Go scanner、rustc 的共同起点:把源码读成 token 流的那一步,至今仍是顺序的——每一字节的状态都依赖前一个字节。多核时代想加速,第一反应是把源文件切成若干段,让 N 个线程同时扫。问题在于:切错一处,前后两个线程错位,token 边界立刻错位。

2026 年 8 月,arXiv:2608.03473 提出一个冷峻的方案——certified split points:某字节偏移能被设为并行分片点,必须通过一组位级证明;不能证明的,宁可单线程跑,也不许分割。它还首次给出”丢弃 token”模型下的精确切分词,给并行 lexing 写下了第一个具有机器可证含义的安全契约。

一、为什么词法分析过去五十年没人并起来

DFA 的本质是有限状态自动机,每个 char 都改变当前状态,再读下一个 char。表中 “状态 17” 在输入不同字节后会跳到 “状态 8” 或 “状态 23””,离开前一字节的状态,没有人能猜下一个状态。这也是 lexer 比 parser 难并行的根因——parser 至少还能按函数分块、还能 AST 子树并行;而 lexer 必须看着每一个原始字节。

过去十年业界有过几次尝试,要么要求语法作者加哨兵字符(破坏兼容性),要么用符号执行来回放状态(开销比直接顺序扫还慢)。唯一上规模的实用方案是 Go 1.21 之前实验过的 “chunk scanning”:每块独立从初始态重启,遇到歧义就回退——简单,但对正则前瞻、注释嵌套等场景经常回退到单线程。

二、Certified Split Points 在解什么

新论文的核心是一组可被独立验证的定理:给定 DFA 状态表、起始状态、终止字符集,可以机械地判定某偏移 k 是否安全可切——也就是说,从偏移 k 开始的输入,无论前面半段如何,词法状态都可以被某个确定状态 s_k 接管,且 s_k 与前半段的最终状态无关。满足这条性质时,k 才是 “certified split point”。

听起来抽象,但落地很硬核:当编译器对一段 100 MB 的源码做 “并行预扫” 时,它可以在 O(n) 时间内挑出全部 certified split points,把源码切成 k-1 段让 k 个核同时跑,中间不必来回通信。只要 split 集合是 verified 的,输出 token 流与顺序 lexer 完全等价——这条等价不是口头的,而是机器检查过的证明义务。

三、为什么这一点会”向上漏”

词法层面的差异会一路传到 AST、CFG、IR,最终出现在二进制里。论文用一行 LaTeX 把这点写得刺眼:被错误切分丢掉的 token 永远不会重新进入流——下游分析器拿到的是 “被静默改写后的源”。这意味着基于源码的差分模糊测试、SAN 工具、源码安全扫描、生成测试用例的工具链,全都建立在 lexer 的 “顺序可靠性” 假设之上;假设被打穿,差分覆盖率和回归检测的可信度同步打折。

更细思极恐的是隐私维度:若一份第三方二进制在你的 CI 上对受版权保护的源码做并行 re-lex,被丢弃的 token 可能携带代码注释、字符串字面量——这些不是 “执行行为”,但属于源代码资产。论文给出的 “discarded tokens” 模式正是为此预留:它给了一个”丢弃是可被证明的、不泄信息的”上界,而非黑盒启发式。

四、它意味着什么

短期看,最大受益者是基础设施玩家:LLVM 的 lexer、Go scanner 的下个版本、Rust 的 “parallel lex” POC,都可以直接引用这套切分点做种子。长期看,编译器开始 “承担可证正确性” 的责任——从 lexing 开始一层层向上,再加上 parser 的 GLR 等价证明、optimizer 的 translation validation,整条工具链会把”编译器会偷偷改语义”这个延续几十年的怀疑收敛掉。

对日常工程师也是温和提醒:当你的 fuzz 报告里出现”在这段 chunk 与那段 chunk 边界处 crash”,不要先怀疑业务代码——先看 lexer 的切分点是不是被某个隐藏路径走坏了。


本站编辑整理,资料来源公开网络。如有错误欢迎指正。

admin77

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注