ILAsmの配列

ILAsmの配列がよく理解できませんでした
そこでExpert .NET 2.0 IL Assemblerの内容を適当に訳してまとめした

種類

CLRには2種類の配列があります
vectorとarrayです

1次元配列です。下限値が0に決まっています

  • array

多次元配列です。1次元以上の配列を定義できます。下限値には0以外も指定できます


arrayとvectorは別なものとして扱われます
例えば、2次元のarray(実例: int32[..., ...])とvectorvector(実例: int32)は全く別なものです
int32[..., ...] は1度のインストラクションコールで生成されます
int32は2回のnewarrインストラクションコールで生成されます

ILAsm表記法

vector
[ ]
array
[ [,] ]
 ::= [] ... []
実例
//vector
int32[]


//array
int32[..., ...]
int32[2...5]
int32[0..., 0...]


上限値、下限値とも指定しないときは"..."は省略可能です
そのため2次元以上の配列で int32[..., ...] と int32[,] は同じ意味のarrayになります
しかし1次元配列では、"..."は省略時の意味が分かれます
int32[] はvectorを表します
int32[...] はarrayを表します

もし下限値が指定されなかったときは、0だと仮定されます(?:要確認)
また、2次元以上のarrayで2次元目以上の範囲が指定されたときは、
それ以下の次元の範囲も指定します
例えば5次元のarrayで、3次元目の範囲を指定したら、
1次元目、2次元目の範囲も指定します

実例
int32 [ 0...3 , 0...3 , 0...3 , ... , ... ]   // OK
int32 [ ... , ... , 0...3 , ... , ... ]       // NG、1番目、2番目の範囲が未指定
int32 [ ... , 0...3 , 0...3 , ... , ... ]     // NG、1番目の範囲が未指定
int32 [ 0...3 , ... , 0...3 , ... , ... ]     // NG、2番目の範囲が未指定

インスタンス

vector、arrayとも[mscorlib]System.Arrayクラスから派生されています
vector、arrayともオブジェクト参照なので、使うときにはインスタンス化します
vectorインスタンス化は、newarrインストラクションを使います
arrayインスタンス化は、arrayのコンストラクタを使います
インスタンス化をするときは、そのサイズを指定します
そのため配列のサイズは、配列の型の属性ではなく、配列のインスタンスの属性になります

arrayのインストラクション

ILインストラクションセットにはvectorを扱うものはありますが、arrayを扱うものはありません
arrayやarrayの要素を扱うために、[mscorlib]System.Arrayのメソッドを呼ぶ必要があります
そのメソッドは、Get、Set、Addressです
Getメソッドはインデックスを表すN個の引数をとり、そのインデックスが指す値を返します
Addressメソッドも同様に引数をとり、インデックスが指す要素のマネージドポインタを返します
Setメソッドはインデックスを表すN個の引数と、インデックスが指す要素へ代入する値をとり、voidを返します

array生成

arrayコンストラクタは定義されていない下限値と大きさ(上限値)の数だけパラメータをとります
そのためnewobjインストラクションをコールする前に
同じ数の整数値をスタックに積んでおきます

実例
.locals init(int32[0..., 0...] iArr)
ldc.i4 5    // 1次元目の大きさ
ldc.i4 10   // 2次元目の大きさ
newobj instance void int32[0..., 0...]::.ctor(int32, int32)
stloc iArr

vector生成

newarr <要素の型>

vectorを生成します
このインストラクションはスタックからvectorの要素数を下ろして
生成したvectorの参照を積みます

実例
.locals init (int32[] arr)
ldc.i4 123
newarr int32
stloc.0

vector素数

ldlen

配列の要素数を取得します
このインストラクションはスタックからvectorインスタンスの参照を下ろして
その要素数を積みます

要素アドレス読み込み

ldelema <要素の型>

型のvector要素のアドレスを取得します
このインストラクションはスタックから要素のインデックスとvectorインスタンスの参照を下ろして
その要素へのマネージドポインタを積みます

素読み込み

ldelem.i4

int32型の要素をvectorから読み込みます

ldelem.ref

オブジェクト参照型の要素をvectorから読み込みます

他にも基本的な型の要素を読み込むインストラクションがあります
これらのインストラクションは共通してスタックから要素のインデックスとvectorインスタンスの参照を下ろして
その要素の値を積みます

ldelem (ldelem.any) <要素の型>

型の要素をvectorから読み込みます
このインストラクションは CLR version 2.0から導入され
vectorジェネリック型で定義されたarrayの変数を扱えます
このインストラクションの良いところは、型を自由に指定して読み込めるところです

要素格納

stelem.i4

int32型の要素をvectorに格納します

stelem.ref

オブジェクト参照型の要素をvectorに格納します
このインストラクションはキャストを伴います
そのためInvalidCast exceptionが投入されることがあります

stelem (stelem.any) <要素の型>

型の要素をvectorに格納します
このインストラクションは CLR version 2.0から導入され
vectorジェネリック型で定義されたarrayの変数を扱えます
このインストラクションの良いところは、型を自由に指定して読み込めるところです

サンプルプログラム

// コンパイル方法:
// ファイル名: tmp.il で保存して、SDK コマンド プロンプトから
// > ilasm tmp.il
.assembly extern mscorlib {.ver 2:0:0:0 .publickeytoken = (B7 7A 5C 56 19 34 E0 89)}
.assembly tmp{ }
.field static int32 a
.field static int32 b
.method static public void Main() {
    .entrypoint

    ldc.i4.s 9          // vectorの要素数を積む
    newarr int32        // vectorを生成
    stsfld int32 a    // 変数aに格納

    ldsfld int32 a    // 変数aを読み込み
    ldc.i4.0            // インデックスを積む
    ldc.i4.7            // 値を積む
    stelem.any int32    // 要素に格納

    ldsfld int32 a    // 変数aを読み込み
    ldc.i4.0            // インデックスを積む
    ldelem.any int32    // 要素を読み込む

    call void [mscorlib]System.Console::WriteLine(int32)   // -> 7


    ldc.i4.s 5          // vectorvectorの要素数を積む
    newarr int32      // vectorのvectorを生成
    stsfld int32  b // 変数bに格納

    ldsfld int32 b  // 変数bを読み込み
    ldc.i4.0            // インデックスを積む
    ldc.i4.s 3          // vectorの要素数を積む
    newarr int32        // vectorを生成
                        // 生成したvectorが値として積まれている
    stelem.any int32  // 要素に格納

    ldsfld int32 b  // 変数bを読み込み
    ldc.i4.0            // インデックスを積む
    ldelem.any int32  // 要素を読み込む
    ldc.i4.1            // インデックスを積む
    ldc.i4.s 11         // 値を積む
    stelem.any int32    // 要素を格納

    ldsfld int32 b  // 変数bを読み込み
    ldc.i4.0            // インデックスを積む
    ldelem.any int32  // 要素を読み込む
    ldc.i4.1            // インデックスを積む
    ldelem.any int32    // 要素を読み込む

    call void [mscorlib]System.Console::WriteLine(int32)    // -> 11

    ret
}