经年治世

西风烈,长空雁叫霜晨月

编译器搬了个家,查表模块就失联了

通过真实功能与缺件测试,验证运行资源能随包移动。

系列目录 · 工程与交付 · Read in English 工具在开发目录里运行得很好。把安装包解压到另一处,卷积能过,几个简单算子也能过,直到编译一个非线性激活,Python 才报找不到模块。 “可执行文件不是都打包了吗?” 问题在于,可执行文件不一定是编译器的全部。它可能嵌入 Python,调用某个查表参数生成器。生成器又是随着源码目录存在的资源。如果源码路径在编译时被硬编码,程序就像......

库文件明明在包里,为什么程序还是说找不到?

真实库、SONAME、链接名与运行搜索路径如何连接。

系列目录 · 工程与交付 · Read in English “把缺的动态库复制进去就好了。” 十分钟后,新日志仍然显示加载失败。目录里确实有 libcompute.so.4.2,而且文件大小看起来很正常。排查者甚至给它改了个更顺眼的名字。程序却坚持寻找 libcompute.so.4。 这不是程序挑剔,而是链接阶段与运行阶段使用的名字可能不同。 一个共享库的三张名片 在常见的 ELF 共......

头文件是一版,生成器是另一版:编译器构建里的“多人接力”

头文件、库、生成器和缓存共同决定工具链身份。

系列目录 · 工程与交付 · Read in English “已经把 LLVM 的路径改对了,为什么生成出来的代码还是不能编译?” 排查者打开配置日志:依赖目录正确。再看报错:有个接口找不到。第一反应是继续加头文件。几个文件修好以后,另一个目标又失败了。 这时候值得暂停一下,问一个看似傻的问题:这个构建过程里,真的只有一套 LLVM/MLIR 吗? 一个版本号为什么不够 编译器工程至少有......

“我只是重新编译”,为什么安装目录先消失了?

环境、构建和安装各自负责什么,失败后留下什么。

系列目录 · 工程与交付 · Read in English 一位同事想验证两行修改,运行构建脚本,编译到一半失败。问题不大,先用上次安装的工具继续排查——然后发现安装目录已经空了。 “我按的是构建按钮,为什么旧工具也被收走了?” 这类困惑通常说明,脚本把几个职责揉成了一个动作:加载环境、同步依赖、配置工程、编译、清空安装前缀、安装、裁剪产物。每一步单看都合理,放在一起却很难回答“失败到这里......

Debug 一切正常,Release 却把写入操作“优化没了”

必要写入藏进断言后,关闭检查会改变程序语义。

系列目录 · 工程与交付 · Read in English “模型能编译,只有发布版本找不到新生成的常量。” 这句话很容易把人带进一条昂贵的岔路:优化级别是不是太高?浮点精度是不是变了?链接器是不是把某段逻辑丢掉了?如果再补充一句“Debug 完全正常”,优化器几乎已经坐上被告席。 但这一次,最值得看的未必是汇编,而是一个看起来极其负责的断言。 一行代码为什么能决定两种程序 设想编译器把......

少算一点,为什么反而不敢优化了?

优化停用也是设计决定:切片、量化与参数尺寸的契约。

系列目录 · 资源与调度 · Read in English “矩阵乘完再切一小块,不如先切权重,再做小矩阵乘。” “公式没错。切出来的新权重,用哪一组量化参数?” “参数不是跟名字走的吗?” 白板上的代数变换往往十分干净。可编译器中的值除了形状和元素,还背着尺度、零点、通道轴、布局、存储和来源信息。省下的乘法如果把这些信息甩在身后,优化就会从“更快”变成“更难发现错误”。 从一条正确的浮......

“数字已经读出来了”,并不代表输入合法

解析成功之后,仍要检查完整消费、符号与字段范围。

系列目录 · 资源与调度 · Read in English “寄存器写成了 r7oops,怎么还能继续往下走?” “解析函数成功读出了 7。” “后面的 oops 呢?” “它没问。” 编译器后端里,很多错误不是没做检查,而是检查回答了一个比预期更弱的问题。解析出一个整数,只证明字符串开头存在可解析片段,不证明整个字段满足语法;把立即数拆成高低两段,也只证明公式看起来合理,不证明边界处没有......

循环只多了一层,为什么二进制快照像换了一个程序?

循环、标签和独立发射实例如何影响二进制确定性。

系列目录 · 资源与调度 · Read in English “刚才这个测试还过,单独跑也过,放到整套测试后面就变了。” “是不是随机种子?” “没有随机数。只有一个静态计数器。” 有时编译器的不确定性不来自复杂并行算法,而来自一个朴素到容易被忽略的全局状态:为循环标签编号的计数器。第一个测试用掉一些编号,第二个测试就从更大的数字开始;换个执行顺序,汇编文本又不同。 标签应该属于谁 标签用......

catch 住异常之后,半份机器码怎么办?

错误被捕获之后,已产生的状态与输出仍需明确处理。

系列目录 · 资源与调度 · Read in English “异常已经被 catch 了,编译器不会崩。” “那输出目录里为什么有一半文件?” “因为写文件发生在另一条路径。” 代码生成的失败,很少只发生在一个漂亮的 try 块里。形状检查可能失败,描述符字段可能无法编码,依赖计数可能不合法,直到最后导出二进制,才发现某条指令装不进目标格式。把异常接住只是第一步;下一步是决定失败以后,调用......

等过了事件,为什么还没等到数据?

跨执行单元传递状态时,事件必须覆盖真正的数据生产。

系列目录 · 资源与调度 · Read in English “我已经加了等待,怎么还能读到没算完的门值?” “你等的是哪个事件?” “一个 ready 事件。” “它到底覆盖了哪条真实工作?” 最后这个问题,往往比前面十行同步代码都重要。事件名字表达的是愿望,设备执行的是协议。一个叫“数据就绪”的标记,只有与生产数据的真实命令建立了正确关系,才真的能代表数据就绪。 循环单元天然有两种并行......