[過去ログ]
C#, C♯, C#相談室 Part96 (1002レス)
C#, C♯, C#相談室 Part96 http://mevius.5ch.net/test/read.cgi/tech/1639965805/
上
下
前次
1-
新
通常表示
512バイト分割
レス栞
抽出解除
必死チェッカー(本家)
(べ)
自ID
レス栞
あぼーん
このスレッドは過去ログ倉庫に格納されています。
次スレ検索
歴削→次スレ
栞削→次スレ
過去ログメニュー
137: デフォルトの名無しさん (ワッチョイ a236-8qwV) [sage] 2022/03/15(火) 17:49:18.74 ID:uT8cdwkS0 相談させてください。 IntPtr を ref int に変換するために以下のようなコードを書くと、期待通り False と表示されます。 IntPtr ptr = Marshal.AllocCoTaskMem(4); ref int x = ref Unsafe.AddByteOffset(ref Unsafe.NullRef<int>(), ptr); Console.WriteLine(Unsafe.IsNullRef(ref x)); // False と表示される Marshal.FreeCoTaskMem(ptr); しかし、以下のように意味のない for 文を追加すると、コードの最適化が有効な場合のみ True と表示されます。 for (int i = 0; i < 0; i++) { } // 意味のない for 文 IntPtr ptr = Marshal.AllocCoTaskMem(4); ref int x = ref Unsafe.AddByteOffset(ref Unsafe.NullRef<int>(), ptr); Console.WriteLine(Unsafe.IsNullRef(ref x)); // 最適化が有効な場合のみ True と表示される Marshal.FreeCoTaskMem(ptr); ただし、意味のない for 文があっても Unsafe.AddByteOffset(ref Unsafe.NullRef<int>(), ptr) → Unsafe.SubtractByteOffset(ref Unsafe.NullRef<int>(), -(nint)ptr) のように書き換えると常に False と表示されるようになります。 なぜこのようなことが起こるのかさっぱり見当がつかないので、お知恵を拝借できないでしょうか。 私の環境を分かる範囲で書くと以下のとおりですが、他に何か必要な情報があればお教えください。 Windows 10 Pro (21H2) Microsoft Visual Studio Community 2022 (64 ビット) Version 17.1.1 コンソール アプリケーション、.NET 6.0 どうぞよろしくお願いいたします。 http://mevius.5ch.net/test/read.cgi/tech/1639965805/137
143: デフォルトの名無しさん (ワッチョイ a236-8qwV) [sage] 2022/03/15(火) 21:23:35.68 ID:uT8cdwkS0 皆様、返信どうもありがとうございます。 いただいたアドバイスを元に色々と確認をしていて反応が遅くなってしまいました。 申し訳ありません。 >>138 確認してみた所、C# では int は 32 ビットと決められているようです。 https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/builtin-types/integral-numeric-types ただ、可読性を考えると 4 ではなく sizeof(int) と書くべきでした。 ご指摘どうもありがとうございました。 >>139 Unsafe.NullRef<T>() と Marshal.ReadInt32(int) の実装を確認してみたところ、 おそらくその点は問題ないかと思います。 https://github.com/dotnet/corert/blob/master/src/System.Private.CoreLib/shared/Internal/Runtime/CompilerServices/Unsafe.cs https://github.com/dotnet/corert/blob/master/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/Marshal.cs しかし、問題の原因は大抵こういう思い込みに隠れているものだと思うので、 可能性を一つ潰すことができてとても助かりました。 >>140 そうなんですよね。 for (int i = 0; i < 0; i++) {} は i++ に到達しないのは明らかだから 最適化でまるっと消えてしまうかと思っていたので、この結果には驚きました。 >>141 書いていただいたコードを試してみたところ、確かに最適化が有効でも期待通りの動作になりました。 それからもう一つ、書いていただいたコードを使わない場合、 プラットフォームが x86 と x64 の両方とも最適化有効時には期待通りの動作をしないことが分かりました。 (x86 の場合は常に期待通りに動作するならば問題の原因について一つ仮説が立てられるかと思ったのですが、 実際は違っていたので未だに原因はさっぱり見当がついていません…) http://mevius.5ch.net/test/read.cgi/tech/1639965805/143
144: デフォルトの名無しさん (ワッチョイ a236-8qwV) [sage] 2022/03/15(火) 21:24:27.40 ID:uT8cdwkS0 >>142 null参照への演算が未定義というのは、AddByteOffset メソッド内の話でしょうか。 下記のページの AddByteOffset<T>(ref T, IntPtr) のところをみると ldarg.0 ldarg.1 add ret とコメントされていて、少なくとも IL 的には一つ目の引数が null でも特に問題はないように思えてしまいます。 IL 的に問題がないかどうかは私は自信がないのですが、 もし IL に問題がないのに JIT 最適化で問題が起きてしまうとすれば、 バグと考えてもよいのでしょうか。 参考 https://github.com/dotnet/corert/blob/master/src/System.Private.CoreLib/shared/Internal/Runtime/CompilerServices/Unsafe.cs http://mevius.5ch.net/test/read.cgi/tech/1639965805/144
149: デフォルトの名無しさん (ワッチョイ a236-8qwV) [sage] 2022/03/15(火) 22:07:11.34 ID:uT8cdwkS0 >>145 そうですね。アドバイスありがとうございます。 私はこれまでこの手の問題は「まずは自分を疑え」と教わってきて 実際それで大抵の問題は解決してきたのですが、 皆様のお話をうかがうに、今回は自分の勘違いではなく よそに原因があると考えたほうがいいのかもしれません。 これまで報告等は経験がないのですが、 それも視野に入れて調べてみたいと思います。 >>146 勉強になります。 その特殊な最適化で結果が変わることがバグなのか 私の力では判断が難しそうなのが歯がゆいところです。 >>147 > そう、片方が0なら実質意味はないから加算命令が現れなくても不思議ではないけど > Console.WriteLine(x);としてみても固定アドレス0をデリファレンスするので 私の理解不足で、 「片方が0なら実質意味はない」 ← 0 + p の計算は必要ない?そりゃそうだ! 「固定アドレス0をデリファレンスする」 ← 0 + p = 0 ということ?p ではなくて? という感想を持ってしまいました。 申し訳ないのですが、もしよろしければもう少し詳しく説明していただけないでしょうか。 > C#的には未定義なら未定義でビルド時か実行時にエラーを出しそうだけど > Unsafeでチェックされないのか判断に困る。さもなければ最適化のバグだと思う ご意見どうもありがとうございます。とても共感させていただきました。 未定義であればこそデバッグ時に教えてほしいものですが、 実際にはデバッグ時には期待通りに動作してしまうのが厄介なところです。 http://mevius.5ch.net/test/read.cgi/tech/1639965805/149
メモ帳
(0/65535文字)
上
下
前次
1-
新
書
関
写
板
覧
索
設
栞
歴
スレ情報
赤レス抽出
画像レス抽出
歴の未読スレ
AAサムネイル
Google検索
Wikipedia
ぬこの手
ぬこTOP
0.040s