- PR -

VBコンパイラの最適化により挙動が異なる

投稿者投稿内容
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2007-02-28 19:28
現象確認しました。
For i As Integer = 0 To 2
dim j=i * 2 + 1
If testArray(i * 2 + 1).Length = 1 Then
Console.WriteLine(testArray(i * 2 + 1))
End If
Next


をかませば、期待どおりになりました

改行が変なのは携帯からのため。ごめんなさい
_________________
よねKEN
ぬし
会議室デビュー日: 2003/08/23
投稿数: 472
投稿日時: 2007-02-28 22:57
.NET Framework1.1、2.0の各環境で、
同じilアセンブラソースを元にilasmでビルドして実行してみました。

実験結果は次のとおりでした。
1.1ではOut1、Out2共に正しい結果、
2.0ではOut2は間違った結果になりました。

予想通りでしたが、VBコンパイラの最適化の問題ではなく、
それよりも低レベルな層での問題のようです。

となると.NET上で動作するあらゆるアセンブリで同じ問題が
発生する可能性があるということに・・・

実験に使ったビルドできるILコードは以下です。
#コマンドは以下で
#ilasm OptimizeTest.il

コード:
.assembly OptimizeTest
{
    .ver 0:0:0:0
    .hash algorithm 0x00008004
}

.class public auto ansi OptimizeTest
    extends [mscorlib]System.Object
{
    .method public specialname rtspecialname instance void .ctor() cil managed
    {
        .maxstack 8
        L_0000: ldarg.0 
        L_0001: call instance void object::.ctor()
        L_0006: ret 
    }

    .method public instance void Execute() cil managed
    {
        .maxstack 3
        .locals init (
            string[] textArray1,
            string[] textArray2)
        L_0000: ldc.i4.6 
        L_0001: newarr string
        L_0006: stloc.1 
        L_0007: ldloc.1 
        L_0008: ldc.i4.0 
        L_0009: ldstr "a"
        L_000e: stelem.ref 
        L_000f: ldloc.1 
        L_0010: ldc.i4.1 
        L_0011: ldstr "b"
        L_0016: stelem.ref 
        L_0017: ldloc.1 
        L_0018: ldc.i4.2 
        L_0019: ldstr "c"
        L_001e: stelem.ref 
        L_001f: ldloc.1 
        L_0020: ldc.i4.3 
        L_0021: ldstr "d"
        L_0026: stelem.ref 
        L_0027: ldloc.1 
        L_0028: ldc.i4.4 
        L_0029: ldstr "e"
        L_002e: stelem.ref 
        L_002f: ldloc.1 
        L_0030: ldc.i4.5 
        L_0031: ldstr "f"
        L_0036: stelem.ref 
        L_0037: ldloc.1 
        L_0038: stloc.0 
        L_0039: ldstr "Out1"
        L_003e: call void [mscorlib]System.Console::WriteLine(string)
        L_0043: ldarg.0 
        L_0044: ldloc.0 
        L_0045: callvirt instance void OptimizeTest:ut1(string[])
        L_004a: ldsfld string [mscorlib]System.String::Empty
        L_004f: call void [mscorlib]System.Console::WriteLine(string)
        L_0054: ldstr "Out2"
        L_0059: call void [mscorlib]System.Console::WriteLine(string)
        L_005e: ldarg.0 
        L_005f: ldloc.0 
        L_0060: callvirt instance void OptimizeTest:ut2(string[])
        L_0065: ldsfld string [mscorlib]System.String::Empty
        L_006a: call void [mscorlib]System.Console::WriteLine(string)
        L_006f: ret 
    }

    .method public static void Main() cil managed
    {
        .custom instance void [mscorlib]System.STAThreadAttribute::.ctor()
        .entrypoint
        .maxstack 1
        .locals init (class OptimizeTest test1)
        L_0000: newobj instance void OptimizeTest::.ctor()
        L_0005: stloc.0 
        L_0006: ldloc.0 
        L_0007: callvirt instance void OptimizeTest::Execute()
        L_000c: ret 
    }

    .method private instance void Out1(string[] testArray) cil managed
    {
        .maxstack 3
        .locals init (
            int32 num1)
        L_0000: ldc.i4.0 
        L_0001: stloc.0 
        L_0002: ldarg.1 
        L_0003: ldloc.0 
        L_0004: ldc.i4.2 
        L_0005: mul.ovf 
        L_0006: ldc.i4.1 
        L_0007: add.ovf 
        L_0008: ldelem.ref 
        L_0009: call void [mscorlib]System.Console::WriteLine(string)
        L_000e: ldloc.0 
        L_000f: ldc.i4.1 
        L_0010: add.ovf 
        L_0011: stloc.0 
        L_0012: ldloc.0 
        L_0013: ldc.i4.2 
        L_0014: ble.s L_0002
        L_0016: ret 
    }

    .method private instance void  Out2(string[] testArray) cil managed
    {
        // コード サイズ     38 (0x26)
        .maxstack  3
        .locals init (int32 V_0)
        IL_0000:  ldc.i4.0
        IL_0001:  stloc.0
        IL_0002:  ldarg.1
        IL_0003:  ldloc.0
        IL_0004:  ldc.i4.2
        IL_0005:  mul.ovf
        IL_0006:  ldc.i4.1
        IL_0007:  add.ovf
        IL_0008:  ldelem.ref
        IL_0009:  callvirt   instance int32 [mscorlib]System.String::get_Length()
        IL_000e:  ldc.i4.1
        IL_000f:  bne.un.s   IL_001d
        IL_0011:  ldarg.1
        IL_0012:  ldloc.0
        IL_0013:  ldc.i4.2
        IL_0014:  mul.ovf
        IL_0015:  ldc.i4.1
        IL_0016:  add.ovf
        IL_0017:  ldelem.ref
        IL_0018:  call     void [mscorlib]System.Console::WriteLine(string)
        IL_001d:  ldloc.0
        IL_001e:  ldc.i4.1
        IL_001f:  add.ovf
        IL_0020:  stloc.0
        IL_0021:  ldloc.0
        IL_0022:  ldc.i4.2
        IL_0023:  ble.s    IL_0002
        IL_0025:  ret
    } // end of method OptimizeTest:Out2
}

KI
大ベテラン
会議室デビュー日: 2007/01/10
投稿数: 239
投稿日時: 2007-02-28 23:53
よねKENさん。詳しく調査して頂いて本当にありがとうございます。

ということは、今回のコードは最適化を有効にしたときに、
たまたまそのバグが発生するILコードにコンパイルされたのですね。
つまり VBのコンパイラではなく、.NET Framework 2.0 のバグの可能性が高いですね。

それにしても、最適化を有効にしてリリースする場合は、
最適化を有効にした状態でも十分なテストが必要ということですね…
勉強になりました。

#本件、MSDNフォーラムにフィードバックした方がよろしいのでしょうか?
#フィードバックしたことがありませんので、よくわからないのですが。
囚人
ぬし
会議室デビュー日: 2005/08/13
投稿数: 1019
投稿日時: 2007-03-01 00:32
Windows Vista x64 の .NET Framework 3.0(CLR は 2.0 だけど)では現象再現しません。
全く同じアセンブリを Windows XP x86 の .NET Framework 2.0(CLR は勿論同じ2.0) で実行したら現象再現します。完全なる不具合ですね。

Vista の CLR 2.0 が直っているのか、x64 だから直っているのかは不明です。(多分後者のはずですが)

_________________
囚人@わんくま同盟

[ メッセージ編集済み 編集者: 囚人 編集日時 2007-03-01 00:40 ]
よねKEN
ぬし
会議室デビュー日: 2003/08/23
投稿数: 472
投稿日時: 2007-03-01 00:37
引用:

KIさんの書き込み (2007-02-28 23:53) より:

ということは、今回のコードは最適化を有効にしたときに、
たまたまそのバグが発生するILコードにコンパイルされたのですね。
つまり VBのコンパイラではなく、.NET Framework 2.0 のバグの可能性が高いですね。



.NET Framework 2.0のバグの可能性が高いと思ったので(個人的にはほぼ黒)、
そのように言っていましたが、確定的ではないので以下のように訂正します。

○調査結果からわかったこと。
・バージョン1.1と2.0で動作が異なるので、どちらかの動作が間違っている可能性が高い
・この動作の違いは、1.1→2.0での仕様変更という可能性もあるが、
提示のVBソースの内容、及び、私がILコードそのものを机上デバッグした限りでは、
バージョン1.1の動作が正しいように見受けられる。

で、このことからありえる可能性は以下の2パターンだと思います。
1. バージョン2.0のILより低いレベルの層で不具合がある。
2. 動作の違いは、1.1→2.0での仕様変更であり、VBコンパイラの出力しているILが不正である。
(ilasmで警告もでなければ、実行時にも例外さえ起きないので可能性は低そう)

引用:

#本件、MSDNフォーラムにフィードバックした方がよろしいのでしょうか?
#フィードバックしたことがありませんので、よくわからないのですが。



ぜひ、お願いします。

Visual Studio フィードバック

Visual Studioそのものの問題ではないけど、こちらでいいのでしょうか?
ご存知の方、フォローお願いします。
#私もよく知らないので(^^;
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2007-03-01 00:43
_Ovf 付きの演算の最適化がどうにもアレな感じ。
/removeintchecks+ 付けてやれば再現しなくなるし、C# ではデフォルトで _Ovf 付かないから等価のコードを書いても再現しないけど checked ステートメント使ったりすると再現するようになったり。
聞いたことがあるような、と思ったら別の現象を
http://hongliang.seesaa.net/article/14766859.html
に自分で書いてました。
よねKEN
ぬし
会議室デビュー日: 2003/08/23
投稿数: 472
投稿日時: 2007-03-01 00:52
引用:

囚人さんの書き込み (2007-03-01 00:32) より:
Windows Vista x64 の .NET Framework 3.0(CLR は 2.0 だけど)では現象再現しません。
全く同じアセンブリを Windows XP の .NET Framework 2.0(CLR は勿論同じ2.0) で実行したら現象再現します。完全なる不具合ですね。



貴重な情報ありがとうございます。
同じ2.0で再現しないのであればもう黒で間違いないですね。

追加情報ですが、
先のILコードで適当なところでローカル変数に意味のない代入をするとか(KIさん、Jittaさんも指摘されいるVBソース上でもできる回避方法ですね)、
add.ovf命令を代わりにaddまたはadd.ovf.unに変更するだけでも再現しなくなります。
KI
大ベテラン
会議室デビュー日: 2007/01/10
投稿数: 239
投稿日時: 2007-03-01 21:24
MSDNフォーラムにフィードバックしておきました。

https://forums.microsoft.com/MSDN-JA/ShowPost.aspx?PostID=1288395&SiteID=7

スキルアップ/キャリアアップ(JOB@IT)