[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fhMg4wYRRa6MGBt4yk0BiID8p_2Bc9oyl6ROLqKF2dNQ":3},{"item":4,"related":48},{"id":5,"type":6,"title":7,"slug":8,"summary":9,"body":10,"coverUrl":11,"productScreenshots":12,"productLinks":13,"authorName":14,"authorUrl":15,"authorSubject":16,"category":17,"tags":22,"sourceLabel":39,"sourceName":40,"sourceUrl":41,"status":42,"seoTitle":7,"seoDescription":9,"canonicalUrl":39,"isFeatured":43,"sno":44,"sortOrder":45,"publishedAt":46,"updatedAt":47,"createdAt":47},"5dd4da13-5b3c-449f-8a47-c0042cb441bc","article","Dominator Tree：一条程序路径上谁是必经之地？","dominator-tree-control-flow-explained","Dominator Tree 把程序控制流中“所有路径都必须先经过”的关系显式化。本文用道路地图解释支配关系，说明它如何服务 SSA、循环优化和静态分析，也帮助读者检查复杂条件代码的真正前置条件。","在一段程序里，有些代码块是“必经之路”：不管从入口走哪条合法路径，想抵达后面的某个位置，都必须先经过它。编译器把这种关系称为 dominance，并可以把它组织成 Dominator Tree，也就是支配树。\n\n## 用地图理解支配关系\n\n把函数的控制流想成一张道路地图，入口是城市大门，基本块是路口。若从大门到路口 B 的所有道路都要经过路口 A，那么 A 就支配 B。入口天然支配所有可达位置，而越靠近入口、越被许多路径共享的节点，支配范围通常越大。\n\n支配树不是原始控制流图的复制品。它只保留“谁是某个节点最近的必经上游”这层关系，因此可以帮助编译器快速回答：一条定义在某个使用点之前是否总是可用？某个检查是否覆盖了所有后续路径？循环入口在哪里？\n\n## 它如何帮助优化\n\n在 SSA 形式中，变量定义必须满足支配关系：定义点应该支配使用点，否则某条路径可能还没执行定义就使用了变量。编译器还会利用支配树进行代码提升、公共子表达式消除、循环分析和控制流简化。\n\n如果控制流发生变化，支配树也要及时更新。LLVM 的实现提供了重新计算、插入边、删除边和查询最近公共支配者等能力。这些细节说明，支配关系不是一次性画出来的静态图片，而是优化过程中会持续变化的分析结果。\n\n## 它和调用图有什么区别\n\n调用图关注“哪个函数调用了哪个函数”；支配树关注“同一个函数内部，哪些控制流节点是另一些节点的必经之路”。一个是跨函数的关系，一个是函数内部的路径关系。\n\n理解这个术语，对阅读编译器错误、静态分析报告和 AI 生成的复杂条件代码很有帮助。看到一串嵌套 `if`、循环和提前返回时，可以问：某个检查到底覆盖了哪些路径？它是不是所有危险操作的真正必经前置条件？\n\n## 读者应该记住\n\nDominator Tree 解决的是“谁必须先经过”的问题。它把控制流图中隐含的路径规律显式化，是 SSA、循环优化和代码安全分析的重要基础设施。\n\n资料：[LLVM DominatorTree 文档](https:\u002F\u002Fwww.llvm.org\u002Fdocs\u002Fdoxygen\u002Fclassllvm_1_1DominatorTree.html)；[LLVM 分析 Pass](https:\u002F\u002Fllvm.org\u002Fdocs\u002FPasses.html)","\u002Fuploads\u002F2026-09-20\u002Fe2d91b11-29cd-4d7e-9d38-0283e4b4ef18.jpg",[],[],"Foundit","https:\u002F\u002Ffoundit.cn","foundit-ai-editorial",{"id":18,"name":19,"slug":20,"description":21},"6179d3b6-dc34-4483-9ded-3cd9f1b37a47","科普","abbreviation","介绍各领域新兴概念",[23,27,31,35],{"id":24,"name":25,"slug":26},"144abe77-0dc6-4f66-a176-20bddb1c0bfa","编程","coding",{"id":28,"name":29,"slug":30},"a202d639-99a6-488a-a712-4d4c6ffd7e15","开发","dev",{"id":32,"name":33,"slug":34},"4c2bbea6-eab7-40a8-8447-1de478ff7749","分析","analyse",{"id":36,"name":37,"slug":38},"7c76bfc2-f80f-4ee0-a95d-27bd8708b434","技术","slug",null,"LLVM DominatorTree 文档","https:\u002F\u002Fwww.llvm.org\u002Fdocs\u002Fdoxygen\u002Fclassllvm_1_1DominatorTree.html","published",false,55,0,"2026-09-20T00:00:00.000Z","2026-09-20T03:57:51.929Z",[49,57,65],{"id":50,"type":6,"title":51,"slug":52,"summary":53,"coverUrl":54,"authorName":14,"sno":55,"publishedAt":46,"createdAt":56},"d0700249-defa-43a1-a66a-4455c6889072","ABI：为什么源码能编译，二进制却不能互相调用？","application-binary-interface-abi-explained","ABI 是二进制世界的调用合同，规定参数传递、对象布局、符号命名和异常处理。本文区分 ABI 与 API，解释动态库、C++ 兼容性和跨语言绑定为什么不能只看函数签名。","\u002Fuploads\u002F2026-09-20\u002F33666a48-4e8f-42a3-9ffd-f4c490498689.jpg",42,"2026-09-20T03:58:01.058Z",{"id":58,"type":6,"title":59,"slug":60,"summary":61,"coverUrl":62,"authorName":14,"sno":63,"publishedAt":46,"createdAt":64},"c9f4e936-9533-4b9c-a1de-f03ef09fed37","WAL：为什么数据库要先写日志，再写真正数据？","write-ahead-logging-wal-database-explained","WAL 要求描述数据变化的日志先于数据页持久化，让数据库可以延迟刷写并在崩溃后通过重放恢复。本文用账本和收据解释 REDO、检查点、复制与持久性设置的关系。","\u002Fuploads\u002F2026-09-20\u002F6260c953-345b-4c9d-b3d6-6202c7e5539c.jpg",43,"2026-09-20T03:58:15.082Z",{"id":66,"type":6,"title":67,"slug":68,"summary":69,"coverUrl":70,"authorName":14,"sno":71,"publishedAt":46,"createdAt":72},"41d50774-50df-4e2f-b1b3-ab6d9f329726","Backpressure：生产者太快时，系统怎样不被数据淹没？","backpressure-reactive-streams-explained","Backpressure 让下游处理能力反过来影响上游生产速度，避免异步流水线靠无限缓存硬撑。本文用水管和阀门解释响应式流、需求信号、数据丢弃与容量设计，也说明它和普通限流的区别。","\u002Fuploads\u002F2026-09-20\u002F35c47792-3d4b-46ae-9375-c68e4c53330e.jpg",45,"2026-09-20T03:58:08.835Z"]