Repository navigation
再帰する呼び出しを診断で断つ - #254
再帰する呼び出しを診断で断つ#254
Conversation
…rsiveCall # Conflicts: # CONTRIBUTING.md
PRレビュー / PR review変更 20 ファイル、コミット 7 件。 / 20 files, 7 commits. 検証の要否 / Verification required以下を走らせ、失敗した試験の名前と失敗の様子を変更前と突き合わせてください。総数だけで判断しないでください。 dotnet build ComputeWeave.sln -c Release -p:Platform=x64
dotnet test tests/ComputeWeave.Tests.SourceGenerators/ComputeWeave.Tests.SourceGenerators.csproj -c Release -p:Platform=x64
dotnet test tests/ComputeWeave.Tests.Internals/ComputeWeave.Tests.Internals.csproj -c Release -p:Platform=x64
dotnet test tests/ComputeWeave.Tests/ComputeWeave.Tests.csproj -c Release -p:Platform=x64
dotnet test tests/ComputeWeave.Tests.DeviceLost/ComputeWeave.Tests.DeviceLost.csproj -c Release -p:Platform=x64
dotnet test tests/ComputeWeave.D2D1.Tests.SourceGenerators/ComputeWeave.D2D1.Tests.SourceGenerators.csproj -c Release -p:Platform=x64
dotnet test tests/ComputeWeave.D2D1.Tests/ComputeWeave.D2D1.Tests.csproj -c Release -p:Platform=x64
dotnet test tests/ComputeWeave.D2D1.Tests.AssemblyLevelAttributes/ComputeWeave.D2D1.Tests.AssemblyLevelAttributes.csproj -c Release -p:Platform=x64ビルドエラーを出す診断を変えた場合は、アナライザーの試験に加えて解全体の検証が要ります。 慎重を要する箇所 / Guarded areas touched
寿命の追跡と危険の追跡は別々の保証です。一方を保っても他方が保たれるとは限りません。 公開APIまたは生成される記述子を変えた場合は、該当部分の互換性、生成の決定性、基準データの検査を走らせてください。 確認できた点 / Confirmed
これは自動の確認で、査読の代わりにはなりません。指摘は判断の材料であって、従う義務はありません。 |
…rsiveCall # Conflicts: # CONTRIBUTING.md
Summary / 概要
再帰するメソッドや局所関数がシェーダーの本文にあると、生成器は診断を出さずに書き出し、HLSL には再帰が無いのでシェーダーコンパイラが断っていました。その誤りはシェーダー型の位置へ
CMPW0046(Direct2D 側はCMPWD2D0034)として転送されるだけで、文が名指すのは生成 HLSL の関数です。局所関数と補助型のメソッドでは、その関数名は生成器が付けたものです。書き換えが書き出す呼び出しを、シェーダーごとに1つの集合へ記録するようにしました。全ての宣言を書き換えた後にその集合を読み、書かれた宣言へ戻る呼び出しをCMPW0129(Direct2D 側はCMPWD2D0101)として、著者が書いた呼び出しの位置へ報告します。Linked issue / 関連する課題
Closes #246
Kind / 種別
Behavior change / 挙動の変更
再帰する呼び出しを持つシェーダーの挙動が変わります。これまでは転送の診断がシェーダー型の位置に1件出て、文は HLSL 側の関数名を含んでいました。変更後は環の上にある呼び出しごとに
CMPW0129がその呼び出しの位置へ出て、bytecode は建てられません。文は呼び出し先と、その呼び出しが書かれた宣言を C# の名前で名指します。互いを呼ぶ2つのメソッドでは両方の呼び出しに出るので、どちらを消しても直ります。再帰しないシェーダーは何も変わらず、生成物も同一です(後述の全数比較)。公開 API は増えず、診断の識別子が計算側と Direct2D 側に1つずつ増えます。
Verification / 検証
dotnet build <project> -t:Rebuild -c Release -p:Platform=x64 -p:EmitCompilerGeneratedFiles=trueスイートは、生成器が走る11プロジェクトを1本ずつ
-t:Rebuildで建ててから、建てた dll を直接起こして走らせました。土台は枝の先端44dea9fd(#248 と #241 を含むmainのc050699dを取り込んだもの)です。判定は失敗した試験の名前で行います。失敗の1件は
BokehBlur (Discrete)で、実機の画像比較が正規化差分 0.0002% で落ちたものです。WARP 側の同じ試験は通り、このシェーダーの生成物は後述の全数比較で v2.6.0 と1バイトも違いません。この実行は同じ機械で他の並列の構築が走った直後のもので、実機の画像比較はそうした直後に揺れます。同じ枝の先端で同じスイートだけを回し直すと、合計4189、失敗0、成功4137、省略52で、変更前と同じ値です。その後に入口が自分を呼ぶ行を足した先端98a18b44では、生成器の2スイートを回し直して 593/593 と 237/237 でした。#243 と #253 を含むmain(d6b5bab9)を取り込んだ先端d0006310でも11プロジェクトを建て直して8本を回し、失敗の名前はなく(計算側の生成器 616、Direct2D 側の生成器 240、ComputeWeave.Tests4189/0/52)、生成物の全数も v2.6.0 と同じ 1078/1080 でした。ComputeWeave.Tests.GlobalStatementsは実行可能形式で、Test passed successfully!と終了状態0でした。走らせていないのはComputeWeave.Tests.NativeLibrariesResolverの1本で、出荷経路のワークフローが回します。変更前の値は v2.6.0(a8245449)を同じ手順で建てて測ったもので、8本とも失敗は0件でした。試験の名前で突き合わせると、増えたのは計算側の生成器が35件(この枝の25件と #248 の10件)、Direct2D 側の生成器が5件(この枝の3件と #248 の2件)、ComputeWeave.D2D1.Testsが #247 の2件で、減ったものはありません。生成物の全数比較は、v2.6.0(
a8245449)と枝の先端を別の木で-t:Rebuildし、generated配下の全ファイルの SHA-256 を突き合わせました。両方とも1080ファイルで、1078ファイルが同一、片方にしか無いものは0、違うのは同じコミットを2回建てても動く Direct2D の記述子2件だけです。srcは v2.6.0 から39bf66afまで動いておらず、mainに入った #248 は生成物を1ファイルも変えていません。書き換え器は通しシェーダーコンパイラだけが断る入力を要る既存の試験は、その入力を再帰から変えました。計算側は群が持てる大きさを超える
[GroupShared]の配列、Direct2D 側は画素シェーダーのプロファイルが持たない同期の組み込み(FXC のX3664)です。足した試験は、計算側の生成器に、再帰の19形と、環の無い同じ形の6つの対照、文が著者の名前を名指すことを見る1件です。19形は、自分を呼ぶ静的メソッドとインスタンスメソッド、自分を呼ぶ入口、互いを呼ぶ2つと3つのメソッド、外部の静的メソッド、拡張メソッド、外部の静的メソッドを経てシェーダーのメソッドへ戻る形、補助型のインスタンスメソッド、自分の型を作る構築子、構築子とメソッドが互いを経る形、局所関数の3形(自分を呼ぶ、互いを呼ぶ2つ、メソッドと局所関数が互いを経る)、取り込んだメソッドの中の局所関数、静的フィールドの初期化子から取り込むメソッド、部分メソッド、呼ばれない再帰するメソッドと局所関数です。各行は、報告の識別子が
CMPW0129だけであること(転送が並ばないこと)と、報告の位置の文字列が著者の書いた呼び出しそのものであることを見ます。対照は診断が0件で生成物が空でないことを見るので、シェーダーコンパイラが生成 HLSL を受け付けたことも含みます。Direct2D 側の生成器には静的メソッド、局所関数、互いを呼ぶ2つのメソッドの3形を足しました。変異試験は、実装をコミットしてから7つの形で壊し、生成器の2スイートを全数で起こしました。
7つとも名指しで落ち、対照の6行はどの変異でも通ったままです。土台は乗り物の差し替えと試験を載せた
e3f2dcfeで、その後に取り込んだmainは書き換え器の別の箇所(定数の参照)を動かしています。入口が自分を呼ぶ行はその後に足したので、上の数には入っていません。Adapter and driver tested / 試したアダプターとドライバー
Intel(R) Iris(R) Xe Graphics, 32.0.101.7085
Design notes / 設計の記録
直す層は共有の書き換え器にしました。どの呼び出しが生成 HLSL の関数の呼び出しとして書き出されるかを決めているのはそこで、取り込みと改名もそこにあります。呼び出しは、書かれた宣言から解決先の宣言への辺として記録し、判定は全ての宣言を書き換えた後に行います。書き換えの途中で判定しないのは、書き換えが環の全ての辺を通らないからです。シェーダー自身のメソッドは取り込まれずに生成器の経路で書き出され、局所関数は宣言された場所で持ち上げられ、取り込み済みの宣言は2度目には辿られません。互いを呼ぶ2つのシェーダーメソッドや2つの局所関数の環は、取り込みの経路だけでは見えないということです。記録の集合は、書き換えが集める要求と同じ形でシェーダーごとに1つを全ての書き換え器が共有し、2つの生成器はそれぞれの流れの同じ位置で読みます。要求の型へ入れなかったのは、その型が標本器の使用のようなシェーダーの要求を運ぶものであり、呼び出しの記録はその意味に入らないからです。
報告は環の上の呼び出し全てに出します。1つの環に1件だけ出す形は、深さ優先の順で辺を選ぶことになり、示される呼び出しが辿った順に依ります。全ての辺に出せば、どれを消しても直り、順序にも依りません。
部分メソッドは定義側の部分で記録します。呼び出しの解決先は定義側で、実装側の宣言を書き換えるときに得る記号は実装側なので、揃えないと自分を呼ぶ部分メソッドが環になりません。ソースの無いメソッドの呼び出しは記録しません。既に
CMPW0049が断っており、書き出される関数がありません。触っていない箇所を挙げます。静的フィールドの初期化子は関数ではなく誰からも呼ばれないので、記録の起点にしません。初期化子が取り込むメソッドは、取り込む書き換え器が同じ集合へ記録します。取り込みの印による無限再帰の防止はそのまま残しており、記録はそれより手前で行います。
退けた設計が2つあります。書き換え器に書き換え中の宣言を積む道を持たせ、道の上にある宣言への呼び出しを報告する形は、上の理由で環の一部しか捉えません。全ての宣言を後から意味論的に歩き直して呼び出しの図を作る形は、どの呼び出しが書き出されるかの規則を書き換え器の外にもう一度書くことになり、意味解決も二重になるので採りませんでした。
前提が崩れる筋を書きます。シェーダー自身のメソッドも取り込みの経路で書き出す形へ変えるなら、記録の起点はそのままで判定を書き換えの途中へ移せます。
Not measured / 測っていないこと
Direct2D 側は3形だけを試験にしました。残る形は共有の書き換え器を通るので計算側で測りました。構築子の初期化子(
: this(...))を経る環は C# 自身が断るので建てていません。入れ子の局所関数の再帰は建てていません。Checklist / 確認
If this touches a guarded area / 慎重を要する箇所に触れる場合