2026 年 6 月 26 日,Zig 开发日志上出现了一个标题:「SPIR-V Backend Progress」。作者是 Ali Cheraghi,Zig 编译器 SPIR-V 后端的核心贡献者。这不是一篇宣布「SPIR-V 后端现在可用了」的里程碑文——正相反,它花了大段篇幅承认 bitrot(代码腐化)、单线程限制、以及仅 49% 的行为测试通过率。但同一天这条日志被顶上 Lobsters 首页 28 分,三条评论里全在表达兴奋。
笔者试图理解这股兴奋的来源。一个自托管编译器后端,行为测试通过率不到一半,合并到主分支后多处崩坏需数周修复——从任何传统软件交付标准看,这都该被归类为「早期实验」。社区却从中读出了完全不同的信号:系统编程语言开始在 GPU 领域建立桥头堡。
SPIR-V 在什么位置
要理解这个信号,得先回到 SPIR-V 在 GPU 生态中的位置。
SPIR-V 是 Khronos 集团定义的二进制中间表示(IR),服务于 Vulkan、OpenCL、OpenGL,不久的将来也会被 DirectX 消费。它的核心设计目标很简单:把着色器/计算内核的编译从驱动程序里拿出来,放到应用程序侧。在 SPIR-V 出现之前,GPU 编程的标准路径是——用 GLSL 或 HLSL 写源码文本,交给驱动程序运行时编译。驱动里的编译器质量参差不齐,不同厂商、不同驱动版本的编译结果可以不一样。SPIR-V 改变了这个分工:语言前端负责生成符合规范的 SPIR-V 二进制,驱动只负责将其翻译为 GPU ISA。编译器的责任从驱动转移到了语言工具链。
这一步移动意味着:任何能生成合规 SPIR-V 的编译器前端,都可以成为 GPU 编程的入口。不再需要 GLSL。不再需要 HLSL。Vulkan 规范本身并不关心你的 SPIR-V 二进制是从 GLSL 转译出来的,还是从 C++、Rust、Julia——或者 Zig——直接编译出来的。
这就是为什么编译器后端能输出 SPIR-V 如此重要。一个通用编程语言的编译器可以直接生成 GPU 代码——着色器语言和通用语言的边界开始模糊。
Zig 的后端做到了什么程度
2026 年 6 月 26 日的开发日志覆盖了五个维度的进展。笔者按工程重要性排序:
第一,@SpirvType 内置指令。 SPIR-V 有一些类型没有直接对应 Zig 类型系统——采样器(sampler)、图像(image)、采样图像(sampled image)、运行时数组(runtime array)。过去这些类型只能通过内联汇编手写 SPIR-V 指令来表达,这常年被标记为「写 shader 的最大障碍」。@SpirvType 将 GPU 专用类型直接提升为编译器识别的一等概念,代码里可以用 Zig 语法声明一个采样器、绑定到一个 descriptor set 和 binding 点位——这是从「能生成 SPIR-V 指令」到「能在 Zig 里自然写 shader」的关键跨越。
第二,执行模式改由调用约定承载。 工作组大小、片元原点、网格着色器参数——这些执行模式信息过去通过内联汇编 OpExecutionMode 手动插入。新设计中,你声明一个函数的调用约定为 callconv(.{ .spirv_kernel = .{ .x = 8, .y = 8, .z = 1 } }),编译器自动推导正确的执行模式。同时新增了 spirv_task 和 spirv_mesh 两种调用约定,支持网格着色管线。从用户侧看,声明一个计算着色器的入口函数变得和声明一个普通导出的 Zig 函数一样自然。
第三,多线程代码生成。 SPIR-V 后端从第一天起在链接器线程内单线程运行。这次重构将它融入编译器统一的 MIR → 代码生成流水线,每个代码生成任务和其他自托管后端一样被调度到线程池。一起回归的还有 dedup_types(合并重复类型指令)和 prune_unused(剔除死代码)两个 ISel pass——这两个 pass 在之前单线程重构中被删除,现在因架构升级得以恢复。对用户的实际影响是编译速度,对工程判断而言则意味着 SPIR-V 后端在架构层面退出了「特殊照顾」状态,成为与其他目标平级的编译单元。
第四,对象文件链接。 .spv 文件现在被识别为对象文件格式。多个 .zig 文件(或外部 .spv 对象)可以编译后由 SPIR-V 链接器缝合为单一模块。这意味着大型 shader 项目可以拆分为多个编译单元,理论上支持增量编译和库分发——虽然目前这些高级工作流还未就绪,但格式层面的基础已经打下。
第五,能力和扩展改为从 CPU 特性集驱动。 过去 OpCapability 和 OpExtension 由代码生成或内联汇编 ad hoc 插入,现在改为从 SPIRV-Headers 提取依赖链、统一由 CPU 特性集管理。汇编器拒绝任何手动插入这些指令的尝试——编译器开始对输出正确性承担系统层面的责任,而不是把验证推给下游的 spirv-val 工具。
与四周前相比,行为测试通过率从约 39% 提升到 49%(spirv64-vulkan 目标),修复了数十个 bug,std.gpu 改名 std.spirv。Cheraghi 自己的表述很克制:「SPIR-V 后端比一个月前有意义地更好用了,但还差得远。」
放在竞品地图里看
Zig 不是唯一试图从通用语言打通 GPU 的项目。把几个主要竞品放在一起,能更准确地定位 Zig SPIR-V 后端的位置。
Rust GPU(rust-gpu) 是最直接的参照物。基于 rustc_codegen_spirv,将 Rust 编译为 SPIR-V 着色器。项目始于 2019 年前后,经历了 Embark Studios 的支持和社区移交。目前有基本可用的标准库子集(spirv-std)、浏览器可玩的 SHADERed 演示、实验性 SPIR-T 框架用于链接时优化。但从 GitHub issue 和社区讨论看,Rust 编译器升级常导致 codegen 插件需要跟进适配,稳定版尚未出现。
Circle C++ Shader Compiler 允许用标准 C++ 写着色器,属性标记区分 GPU 入口,编译产物直接是 SPIR-V。语法层面与 CUDA 相近——单源码、C++ 超集。但 Circle 是闭源编译器,依赖 Sean Baxter 个人维护,生态范围受限。
Julia GPU 通过 CUDA.jl 和 AMDGPU.jl 提供 GPU 编程能力,底层绕过 SPIR-V,直接生成 PTX 或 AMDGCN 指令。优势在于交互式开发——REPL 里即时写核然后跑。劣势也明显:跨厂商可移植性依赖包生态而非标准 IR。
Zig SPIR-V 后端在这张地图上的位置很具体:它是唯一一个将 SPIR-V 作为编译器自托管后端的系统编程语言——rust-gpu 是 Rust 编译器的外部 codegen 插件,不是 Rust 项目的一等组件;Circle 是闭源的个人项目;Julia 绕过了 SPIR-V。Zig 的 SPIR-V 后端与 x86、ARM、RISC-V 后端在同一个代码仓库、同一套构建系统里维护,由同一组核心贡献者审查。
这是一把双刃剑。与编译器主线同仓库,意味着 SPIR-V 后端会随着 Zig 编译器的每一次架构调整被动演进——bitrot 就是这种紧密耦合的代价。但也意味着任何针对编译器基础设施的改进(类型系统、代码生成管线、链接器)都可能自动惠及 SPIR-V 后端。6 月 26 日日志中多线程代码生成的恢复,正是这一机制的具体例子:统一 MIR 管线的架构决策使 SPIR-V 后端「免费」获得了线程池调度能力。
真正的障碍不在编译器里
从技术路线图看,Zig SPIR-V 后端面前最大的障碍并不完全在编译器内部。
第一个障碍是地址空间。GPU 内存模型区分 global、local、private、constant 等多种地址空间,而 Zig 的指针默认假设指向 generic 地址空间。Cheraghi 的博客提到,Vulkan 不支持 OpPtrCastToGeneric——所以当前实现把所有指针假定为 Function 存储类作为临时方案。这意味着复杂的指针操作(比如跨地址空间传递引用)在 Vulkan 目标下会受限。在 OpenCL 目标上情况稍好,因为 OpenCL 基线环境保证更多能力,行为测试通过率也更高(约 75%)。
第二个障碍是数值语义差异。Vulkan 环境下的 fma、sqrt、exp、log 等指令不保证正确舍入——这和 Zig 编译器的默认数值语义假设存在冲突。Zig 对确定性的要求高于着色器语言对数值精度的典型容忍度。这不一定是一个不可解决的问题——Rust GPU 和 GLSL 编译器都面对过同样的语义鸿沟——但需要显式的设计决策和文档说明,目前仍在进行中。
第三个障碍是生态层面:标准库适配。GPU 上没有操作系统,没有文件系统,没有堆分配器(至少不是传统意义上的)。Zig 的标准库有大量代码依赖于这些假设。把 std.math、std.sort、常见的数据结构和算法移植到 GPU 友好的子集,是一项规模不亚于编译器后端本身的工作。Cheraghi 在「下一步」清单里列出了前缀和、归约、矩阵乘法等基础算法——这些是 HPC 和 ML 工作负载的基石,说明优先级判断是正确的,但同时也说明进度还处于早期。
为什么这条日志引起了注意
回到 Lobsters 那条 28 分的帖子。技术细节之外的兴奋感有两个来源。
一个来源是时效标记。同一天,Zig 开发日志上还有另一条更新——Matthew Lugg 写的「New @bitCast Semantics and LLVM Backend Improvements」——Lobsters 上单独有 16 条评论。一天之内两条 Zig 编译器进展上榜,在 Lobsters 这种社区里不是常态。它提示的是语言生态的活跃度:Zig 编译器同时在多个维度推进——整数降级、bitCast 语义、LLVM 后端优化、SPIR-V 后端修复——这不是一个只在单一路径上迭代的项目。
另一个来源是方向信号。SPIR-V 后端的存在本身就在说:Zig 的维护者认为系统编程语言应该能编译 GPU 代码。这是声明 GPU 是系统编程的合法疆域——49% 的通过率还不能说「我们也支持 GPU 了」。
这个方向不同于 Rust 的 GPU 故事。Rust 的安全哲学在 GPU 上的差异化价值是明确的——所有权系统可以在编译期防止数据竞争,这在高度并行的 GPU 编程模型中有天然优势。Zig 的价值主张不同:没有隐式分配、编译期计算是同一门语言、对控制流的显式管理。在 GPU 上,没有隐式分配意味着你不会意外触发对不存在的堆分配器的调用。comptime 意味着工作组分发策略、内存布局、展开因子可以在编译期基于 GPU 特性集动态决策——不需要宏,不需要代码生成脚本。
哪一种更适合 GPU 编程?笔者没有立场给出答案。两种语言在 GPU 上的尝试都还太早期,数据的贫乏不支持任何比较性结论。但从生态多样性角度看,存在两种哲学不同的系统语言同时在攻 SPIR-V,比只有一种要好。
谦逊声明
本文的分析基于 2026 年 6 月 26 日 Zig 开发日志、Ali Cheraghi 的「Zig and GPUs」博客文章、Lobsters 社区讨论、以及 Khronos SPIR-V 规范公开文档。笔者未参与 Zig 编译器贡献、未亲自在 spirv64-vulkan 目标下构建和运行着色器。文中引用 49% 行为测试通过率等数据来自开发日志作者自述,未经独立验证。对 Rust GPU、Circle、Julia GPU 现状的描述基于公开仓库、社区讨论和学术论文——各项目的实际可用性可能因使用场景不同而有显著差异。如果你在以上任何领域有直接工程经验,欢迎指出本文的局限。