世界を動かす技術を、日本語で。

Zigの新しいbitCastセマンティクスとLLVMバックエンドの改善

2026年6月25日原文(ziglang.org)

概要

  • Zigメインブランチ2026年の主な変更点まとめ
  • SPIR-Vバックエンドの大幅な修正と機能強化
  • @bitCastの新しいセマンティクス導入とLLVMバックエンド最適化
  • オブジェクトファイルリンクやマルチスレッド対応などの基盤改善
  • さらなるバグ修正とテストカバレッジ向上

2026年6月26日 SPIR-Vバックエンドの進捗

  • SPIR-Vバックエンド、最近のコンパイラ変更で多くの部分が動作不良となり、数週間かけて修正
  • @SpirvTypeビルトイン 導入により、Zig型システムで表現できなかったSPIR-V型を記述可能に
    • シェーダー記述の長年の障壁を解消
    • 詳細は #20550, #23326, #35461 を参照
  • 実行モード情報 (ワークグループサイズ等)は呼び出し規約で管理
    • std.gpu.executionMode()は削除
    • spirv_task, spirv_meshなど新たな呼び出し規約追加
    • OpExecutionModeの手動指定は非許可
  • 機能・拡張の管理 はCPUフィーチャセット駆動に統一
    • SPIRV-Headersから依存関係を抽出
    • OpCapabilityやOpExtensionの直接指定は禁止
  • マルチスレッド対応コード生成 実現
    • 各コード生成ジョブはMir値を生成し、スレッドプールで並列実行
    • dedup_types(型命令の統合)、prune_unused(未使用コード除去)のISelパス復活
  • .spvファイル をオブジェクトファイルとして認識
    • 複数の.zigや外部.spvファイルをSPIR-Vリンカで単一モジュールに統合可能
  • バグ修正多数、spirv64-vulkanターゲットの動作テスト通過率が49%に向上
  • std.gpuはstd.spirvにリネーム
  • SPIR-Vバックエンドの実用度向上、今後もテスト・改善継続
  • バグ報告はCodebergで歓迎

2026年6月25日 新しい@bitCastセマンティクスとLLVMバックエンド改善

  • LLVMバックエンド の整数型ダウングレード方法を最適化
    • 任意ビット幅整数型(u4, i13, u40等)のLLVM IRへの変換方法見直し
    • メモリ格納時はABIサイズ型(i8, i16, i32等)に拡張
    • Clang互換のダウングレード方式を採用し、最適化・誤動作の問題を解消
  • @bitCast の新セマンティクス導入
    • 旧定義(メモリのバイト再解釈)から、論理ビットレイアウトに基づく変換へ変更
    • すべてのバックエンド(LLVM, C, self-hosted)とcomptime実行で統一
    • 標準ライブラリやコンパイラ内部の@bitCast利用箇所も監査・修正
    • 例:配列やベクター型間のbitcastがエンディアン非依存で一貫動作
    • u8→i8や配列→ベクターなど多様な変換が安全・明確に
  • @bitCast関連の提案 も同時実装
    • ポインタベクター型へのbitCast禁止(#18936)
    • enum型へのbitCast許可(一部 #35602)
  • LLVMバックエンドのパフォーマンス向上
    • 新しい整数型ダウングレードにより最適化復活
    • Zigコンパイラ自体で約5%のパフォーマンス向上を確認
  • 0.17.0リリースノート で新セマンティクス・移行方法を解説予定

SPIR-Vバックエンドの主な改善点

  • 型表現力の拡張 :@SpirvTypeで特殊なSPIR-V型を直接記述
  • 呼び出し規約の強化 :シェーダーパイプラインごとに適切な呼び出し規約を用意
  • マルチスレッド最適化 :並列コード生成でビルド時間短縮
  • オブジェクトファイルリンク :複数ファイルの統合が容易
  • テストカバレッジ拡大 :動作確認範囲の大幅増加

@bitCast新セマンティクスのポイント

  • 論理ビットレイアウト による型変換
  • エンディアン非依存 の一貫した動作
  • 配列・ベクター型の柔軟なbitcast が可能
  • 不正な変換の禁止 やenum対応など安全性向上
  • パフォーマンス最適化 にも貢献

Zigは2026年にかけて、 SPIR-V対応の強化型変換セマンティクスの明確化 など、言語基盤の進化が続いています。今後も開発・改善が期待される状況です。

Hackerたちの意見

かなり長い開発ログが来るよ、すみません—ちょっと熱くなっちゃった!mlugg、実際に読みたいものを作ってくれてるんだから謝らないで。低レベルのゴミに溺れてるから、こういう深い技術的な説明は新鮮な風を感じるよ。ゴミコレクターのない言語を作ったことについて謝るべきかもね。ほとんどの人は考えたくないだろうけど、私たちの中にはいいものが好きで、ちゃんと努力する人もいるんだ。

そんなに長くなかったよ!いつもHNのフロントページに載ってるLLM拡張のくだらない話よりずっと短く感じた。

優しい言葉、ありがとう :)

面白い読み物だった、Zigを使ってない私でもね。ところで、この任意幅の整数って…実際に価値があるのかな?私の直感では、むしろ手動でパッキング/アンパッキングした方がいいと思うんだけど(どの言語でも、Cの構造体フィールドのビット幅でも)、実際に生成されるコードのイメージがつかみやすいから。特に符号付きの奇数ビット整数みたいなものだと、符号拡張のためにどんなコードが生成されるのか、普通の操作だと思うけど、誰か他に経験がある人いる?

機械学習で使うファンシーな浮動小数点数を定義するのに最適だよ。例えば、https://github.com/zml/zml/blob/33ced8fa078b3c7c8c709bd526ae...

FPGAエンジニアとして、バイトの倍数じゃないビット幅を扱うのは普通のことで、いろんな理由でソフトウェアを書くときにそれが恋しくなることが多い。特にメッセージをスライスしたり解析したり構築したりする時ね。もちろん、ほとんどのことには回避策があるけど、ビットスライスのためのファーストクラスの言語サポートがあるのはいいことだよ。

確か、"普通"のビット幅の場合、コード生成は基本的に次の大きなマシンタイプを使って、高位のゼロビットを保持するんだ。i3はi8の上位5ビットがゼロの状態("パック"されたi3値にはもっとカスタムな動作があるけど)。それに非ゼロの値を詰めるのは未定義動作だよ。u729のような大きなビット幅の場合、いくつかの大きなマシンタイプを連結して、コンパイラはアンローリングされたループ内で命令を生成するけど、LLVMの最適化パスは通常それをきれいにしない(ただ、整数がLLVMのu729実装を使っていないらしいから、もっと最適化の機会があるかも)。状況によっては便利だけど、パフォーマンスがそれほど重要でない時に特に役立つ。上のu729の例は、新しいパズルを開発するために書いたバリアント数独ソルバーから来たもので、解の空間の大まかな大きさをチェックしたり、実際にボードがどれだけ制限されているかを調べたりするのが簡単だった。最適ではないけど(icacheに厳しいし、レジスタにも負担がかかるし、他にも問題がある)、使うのは簡単だし、アセンブリも悪くない。見かける普通のソルバーよりは全然マシだった。怠惰さ/正確さ/十分なパフォーマンスのパレート曲線上のいいポイントだね。別のコメントでも言われてたけど、奇妙な数値エンティティを表現するためのパックされた構造体には最適だよ(対数的な数体系を持ってると思う)。この言語がよくやることの一つは、コンパイル時に特定の人間のエラーを防ぐために使うことだね。完璧に不可能なアクションを表現できなくするわけではないけど、フルのu32をシフト引数に詰め込むのは普通は意味がないから、型は小さく制約されるんだ。

僕のトイエミュレータープロジェクト(https://github.com/floooh/chipz)では、'システムバス'として使ってて、各ビットが'ワイヤ'になってチップの入出力ピンにマッピングされてるんだ。バス幅は一般的なパラメータで、エミュレートするシステムによって64ビット未満やそれ以上になることもあるよ。任意幅の整数を使うことで、高レベルのコードはバス幅に関係なく同じままで、コンパイラの出力を見ても、ビット操作が基礎の64ビット整数の境界をまたがなければ、そのビット操作はシンプルな64ビット整数で作業するのと同じくらい効率的なんだ。あと、僕の知る限りLLVMはずっと前から任意幅の整数をサポートしてるし、Zigはそれを言語で'公開'しただけなんだよね(後にClangも_ExtInt(N)を通じてやったけど、今はC23の_BitInt(N)に取って代わられて非推奨になってる)。もう一つの良い使い方(エミュレーターでも)として、チップのレジスタやカウンタがあって、これらはしばしば奇数幅(例えば5ビット)を持つから、コードでu5として書く方がu8よりも良いんだ。チップのドキュメントに合ってるし、コードを読むとすぐにこのu5が5ビットのカウンタやレジスタだって分かるからね。

大好き!この言語の一番好きなところの一つだね。例えば、シフトする整数の幅を超えてシフトするのはZigでは違法な動作なんだ。だから、何だっけ、シフトアンド?それでいこう、u64のシフトアンドはu6以下でなきゃならない。面倒くさい?いや、素晴らしいよ!これを見て:const CodeUnitKind = enum(u2) { low, high, follow, lead, }; const CodeUnit = packed struct(u8) { val: u6, kind: CodeUnitKind, }; 本当にいい感じ!

例えば、[2]u8をu16にビットキャストすることを考えてみて。古いセマンティクスでは、この操作の結果はターゲットのエンディアンに依存してた:ビッグエンディアンのターゲットでは、最初の配列要素が8ビットの最上位ビットになり、リトルエンディアンのターゲットでは最初の配列要素が8ビットの最下位ビットになってた。新しいセマンティクスでは、論理的なビット表現だけを気にするから(エンディアンに依存しない)、この操作はすべてのターゲットで同じように動作する:これは大きな間違いだよ。ビットキャストがこんなことをするなんて思わないよ。なんでこんなシンプルで低レベルなものを複雑で高レベルに変えるのか理解できない。u24へのキャストを許可しない方がいいよ、定義しない限りu24をu32サイズにするのは意味がないと思うし、C標準もそうしてると思う。アイデアとしては悪いけど、少なくともこの高レベルなアイデアを実装する別のビルトインを追加して、シンプルな期待と現在の動作を壊さないようにしてほしいな。

これは大きな間違いだよ。ビットキャストがこんなことをするなんて思わないよ。少なくとも@transmuteみたいなものはあるの?Zigが「ビットキャスト」はこの奇妙な操作を意味すると言いたいなら、でもほとんどの人が実際に欲しいものをプラウザブルな名前の下で提供してくれれば、それはただの追加の学習事項で、まあそれはいいと思う。

あなたが言ってる動作には、@bitCastを使う必要はないよ。@ptrCastはまだあるからね。

Hacker Newsで議論の続きを見る