プログラミング
Zig – SPIR-V バックエンドの進捗状況
Zig – SPIR-V Backend Progress (ziglang.org)
要約
Zig言語のSPIR-Vバックエンドが大幅に改善されました。新しい`@SpirvType`組み込み関数が導入され、`callconv`による実行モードの指定が可能になり、CPU機能に基づくCapabilityとExtensionの管理が強化されています。さらに、マルチスレッドでのコード生成やオブジェクトファイルのリンクがサポートされるようになり、シェーダーやコンピュートカーネルでのZigの利用がより実用的になりました。
全文翻訳
開発ログ このページには、mainブランチのZigに対する最近の変更の厳選されたリストが含まれています。RSSフィードでも利用可能です。このページには2026年のエントリが含まれています。他の年のエントリは開発ログアーカイブページで利用可能です。
2026年6月26日 SPIR-V バックエンドの進捗状況 著者:Ali Cheraghi
カバーすべきことはかなりあります。SPIR-Vバックエンドは、最近のコンパイラの変更後、いくつかの場所でビットロットを起こしていたため、過去数週間かけてそれをより良い状態に引き上げることに時間を費やしました。
@SpirvType
SPIR-Vには、Zigの型システムでは表現できないいくつかの型があります。新しい`@SpirvType`組み込み関数は、シェーダーを書く上での最も長年のブロック要因を解決するために導入されました。背景をたどるには、#20550、#23326、#35461を参照してください。
```zig
const Sampler = @SpirvType(.sampler);
const Image = @SpirvType(.{ .image = .{ .usage = .{ .sampled = u32 }, .format = .unknown, .dim = .@"2d", .depth = .unknown, .arrayed = false, .multisampled = false, .access = .unknown, } });
const SampledImage = @SpirvType(.{ .sampled_image = Image });
const RuntimeArray = @SpirvType(.{ .runtime_array = u32 });
const sampled_image = @extern(*addrspace(.constant) const SampledImage, .{ .name = "sampled_image", .decoration = .{ .descriptor = .{ .set = 0, .binding = 1 } }, });
```
呼び出し規約における実行モード
実行モード情報(ワークグループサイズ、フラグメント原点など)は、インラインアセンブリの`OpExecutionMode`を介して出力されるのではなく、呼び出し規約によって運ばれるようになりました。古い`std.gpu.executionMode()`ヘルパーは廃止され、SPIR-Vアセンブラは手動での`OpExecutionMode`命令を拒否するようになりました。メッシュシェーディングパイプライン用に、`spirv_task`と`spirv_mesh`という2つの新しい呼び出し規約も追加されました。
```zig
export fn vert() callconv(.spirv_vertex) void {}
export fn frag() callconv(.{ .spirv_fragment = .{ .depth_assumption = .greater } }) void {}
export fn comp() callconv(.{ .spirv_kernel = .{ .x = 8, .y = 8, .z = 1 } }) void {}
export fn task() callconv(.{ .spirv_task = .{ .x = 1, .y = 1, .z = 1 } }) void {}
export fn mesh() callconv(.{ .spirv_mesh = .{ .stage_output = .output_lines, .max_primitives = 1, .max_vertices = 2 } }) void {}
```
CPU機能からのCapabilityとExtension
CapabilityとExtensionは、これまでコード生成によってアドホックに出力されるか、インラインアセンブリを介して出力されていました。これらは他のターゲットと同様にCPU機能セットによって完全に駆動されるようになり、SPIRV-Headersから依存チェーンが抽出され(外部ベンダーは今のところ除く)、アセンブラは`OpCapability`または`OpExtension`を直接出力しようとする試みを拒否するようになりました。
マルチスレッドコード生成
当初から、SPIR-Vバックエンドはリンカーのスレッド内でシングルスレッドでコード生成を実行していました。現在、各コード生成ジョブは他のセルフホスト型バックエンドと同様にMir値を生成し、コンパイラのThreadPoolにスケジュールされます。
同じ変更により、以前のリファクタリング中に削除されていた2つのISelパスが復活しました:`dedup_types`(同等の型命令をマージする)と`prune_unused`(最終モジュールからデッドコードを削除する)。これらは、コード生成がシングルスレッドだったときに削除されていました。
オブジェクトファイルのリンク
`.spv`ファイルはオブジェクトファイルとして認識されるようになりました。複数の`.zig`ファイル(または外部の`.spv`オブジェクト)をコンパイルし、SPIR-Vリンカーがそれらを単一のモジュールに結合することができます。
この過程で、数多くのバグも修正され、`spirv64-vulkan`ターゲットでの合計合格挙動テスト数が約10%増加し(現在49%)、`std.gpu`は`std.spirv`に名前変更され、SPIR-Vバックエンドは1ヶ月前よりもかなり有用になりましたが、まだ長い道のりです。SPIR-Vではまだ多くの挙動テストがスキップされたままです。とはいえ、シェーダーやコンピュートカーネルでZigを試すことにためらいを感じていたなら、今が試す良い機会です。バグレポートはCodebergで大歓迎です。ハッピーハッキング!
2026年6月25日 新しい`@bitCast`セマンティクスとLLVMバックエンドの改善 著者:Matthew Lugg
(かなり長い開発ログになります、申し訳ありません—これには少し夢中になってしまいました!)
数週間前、私はLLVMバックエンドの改善を実装するブランチで作業を開始しました。これは長い間計画されていたものです。結果的に、いくつかの言語提案を実装するより大きな変更へと発展しました。これらについてお聞きになりたいかもしれません。
LLVMバックエンドの整数ロウリング
Zigは常に、任意のビット幅の整数型(例:`u4`、`i13`、`u40`)をLLVM IRのビット整数型(`i4`、`i13`、`i40`)に直接ロウリングしてきました。しかし、このロウリングが最適ではないことは以前からわかっていました。なぜなら、これらの型をメモリに表現するためのLLVMの文書化されたセマンティクスは、オプティマイザにとって不必要に制限的だからです。おそらくより重要なのは、ClangがこのようなLLVM IRを生成しないため、LLVMのこれらのコードパスが適切にテストされたことがなく、実際にはサポートが不十分であることです—過去数年間、些細な最適化が見逃されたり、あからさまな誤コンパイルが発生したりする事例を多く観察してきました。
したがって、PRの当初の目標は、SSA形式で値を操作する場合にのみこれらのビット整数型を使用し、それらをメモリに格納する際にはABIサイズの型(`i8`、`i16`、`i32`など)にゼロ拡張または符号拡張することでした。これは、ClangがCの`_BitInt(N)`をロウリングする方法と一致するため、十分にサポートされるはずです!
この変更は実際にはかなり簡単でしたが、一つの問題に突き当たり、それが少し回り道をすることになりました。
`@bitCast`の問題
`@bitCast`は興味深い組み込み関数です。以前は、次の一連の操作と同等であると定義されていました。
1. オペランド値へのポインタを取得する
2. それを宛先型へのポインタにキャストする
3. そのポインタからロードする
言い換えれば、これは基本的にメモリのバイトを再解釈するためのシンタックスシュガーでした。しかし、時間の経過とともに、この定義から逸脱していきました—例えば、`[3]u8`を`u24`として再解釈するために`@bitCast`を使用することが許可されるようになりました。ほとんどのターゲットでは`@sizeOf(u24)`が`@sizeOf([3]u8)`より大きいため、上記の定義では不正な動作が発生します。
これまで、LLVMバックエンドは`@bitCast`組み込み関数に対して、これらの不明確なセマンティクスを実装していました。しかし、その定義にはメモリの再解釈が含まれていたため、整数型のメモリへの格納方法を変更すると、`@bitCast`の実装に影響を与え、コンパイラのテストスイートでクラッシュを引き起こす不正な動作を導入することになりました。
これに対する最も簡単な解決策は、おそらくLLVMバックエンドに古い動作に近似するロジックを実装することだったでしょう。私は代わりに、より良い解決策を選択しました—`@bitCast`の新しい定義を実装することです。
`@bitCast`の再定義
2024年、Jacob Youngは`@bitCast`の問題を解決するために、その新しいセマンティクスを正確に指定することを目的とした言語提案#19755をまとめました。この提案は提出後すぐに承認され、実際、その詳細が示されたセマンティクスはすでにセルフホスト型の`x86_64`バックエンドによって実装されています!したがって、LLVMバックエンドの問題を解決するために、必ずしも古い`@bitCast`セマンティクスに合わせる必要はなく、代わりに、この時が新しいセマンティクスをどこでも最終的に実装する良い機会だと考えられました。
余談ですが、これを行うもう一つの利点は、コンパイラの`Legalize`パスを利用できることです。これは、ロウリングが困難な操作をより単純な操作に書き換え、コンパイラバックエンドがそれらの単純な操作のみをサポートすればよいようにするものです。`Legalize`には、セルフホスト型の`x86_64`バックエンドで使用されていた、複雑な`@bitCast`操作をより単純なものに変換する機能がすでにあり、他のコンパイラバックエンド(主にLLVMおよびCバックエンド)を支援するためにも簡単に適応させることができました—ただし、彼らが新しいセマンティクスを実装している場合に限ります。
いずれにせよ、私はコンパイラ全体にこれらの新しいセマンティクスを実装するためのサイドクエスト(これは元のクエストよりも困難であることが判明しました)に乗り出しました。これには、LLVMおよびCバックエンドだけでなく、コンパイル時実行も含まれます—結局のところ、Zigではコンパイル時にほとんどすべての操作が可能であり、`@bitCast`も例外ではありません!新しいセマンティクスは古いものと大きく異なるため(これについては後述します)、標準ライブラリ、コンパイラ、およびサポートライブラリ(例:`compiler_rt`)全体で`@bitCast`の使用箇所の多くを監査する必要もありました。しかし、その後