SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

Microsoft .NET ILアセンブラ入門

.NET Framework SDK付属のアセンブラを使った開発

ディレクティブと命令

 アセンブリ言語の構文は、大きくディレクティブ(directive)と命令(instruction)に分類できます。ディレクティブは、アセンブラがプログラムの構造を認識するために用いられるもので、メンバなどを宣言するために使われます。ディレクティブは必ずドットから始まるルールを持ちます。これまでの.assemblyや.method、.entrypointなどはディレクティブです。同じように、クラスやフィールドの宣言にもディレクティブが用いられます。

 命令は、メソッドの本体に記述します。命令は、ldstrやcallのような、命令の意味を表す単純な1単語とパラメータで構成されます。通常のアセンブリ言語と同じように、命令には対応するオペコードがあり、アセンブリ言語の命令を理解することで、中間言語そのものをコンパイラなしで扱うことも不可能ではなくなります。.NET Frameworkには、オペコードから動的にメソッドを生成する機能などもあり、実行時レベルのメタプログラミングなど、高次元のロジック制御が可能になるかもしれません。

簡単な計算

 アセンブリ言語での命令は、高水準言語のような直観的なものではありません。多項式を一度の命令で表現することはできず、加算、減算など、これ以上分解できない最もシンプルな命令を並べることで複雑なプログラムを記述します。高水準言語のような気の利いたコンパイル時のチェックは行われません。不正なプログラムですら自由に書くことができるため、加算命令ですら状況によっては例外を発生させることになります。

 計算は、スタックに積まれている値に対して行われます。すべてのメソッドにはデータを読み込むためのスタックが用意され、計算やデータの受け渡しにはスタックが用いられます。スタックに詰める要素の数は無限ではありません。各メソッドごとにスロット数を定めることができます。スタックのスロット数は.maxstackディレクティブを使います。このディレクティブは、メソッドのブロック内に指定します。

.maxstackディレクティブ
.maxstack Int32

 パラメータには、スタックのスロット数を指定します。これまでのサンプルのように.maxstackディレクティブを指定しなかった場合、自動的に8スロットが設定されます。これまでのサンプルをILDASMで逆アセンブルしたコードを見ると、自動的にメソッドに.maxstackが設定されていることを確認できます。

 当然、スタックに対するデータのプッシュやポップ処理は実行時に判定されます。よって、スタックが確保しているスロット数を超えてデータをプッシュすると、実行時例外が発生します。これは、アセンブル時には判断できないため、開発者が管理する以外にありません。例えば、スタックに適切なデータが積まれていない状態でWriteLine()メソッドを呼び出すと、実行時にSystem.InvalidProgramExceptionが発生します。

 文字列リテラルはldstrで読み込みましたが、数値は異なる命令でスタックに読み込みます。整数リテラルや浮動小数点数リテラルをスタックに読み込むには「ldc」から始まる命令を使います。この命令はldcに続いてドット記号の後に型を表す文字が続きます。32ビット整数の場合はldc.i4、64ビット整数の場合はldc.i8、32ビット浮動小数点数の場合はldc.r4、64ビット浮動小数点数の場合はldc.r8を使います。この場では、32ビット整数の例を見てみましょう。

ldc.i4命令
ldc.i4 num

 パラメータnumに、スタックに読み込む値を指定します。この命令が実行されると、メソッドのスタックに値がプッシュされ、他の命令で値が用いられるとポップされます。前述のメソッドの呼び出しなどでも、メソッドがパラメータを受け取る場合は、スタックに積まれている値が順にポップされます。例えば、次のように連続してメソッドを呼び出すことも可能です。

ldc.i4 10
ldc.i4 100
call void [mscorlib]System.Console::WriteLine(int32) //100
call void [mscorlib]System.Console::WriteLine(int32) //10

 上記のコードは、以下のコードと同じ振る舞いです。

ldc.i4 100
call void [mscorlib]System.Console::WriteLine(int32) //100
ldc.i4 10
call void [mscorlib]System.Console::WriteLine(int32) //10

 通常、C#言語コンパイラなどで出力した中間言語は、後者のコードのようにメソッドを呼び出す直前に、必要な分だけパラメータをスタックに読み込む形になるでしょう。ところが、アセンブリ言語は、使うと予想されるデータを事前にスタックに読み込み、その後、連続してメソッドを呼び出すといった変則的なコードを書くことができます。

 多くの場合、中間言語を知り尽くした技術者によって開発されたコンパイラが生成するコードの方が効率的で、パフォーマンスにおいても優れた結果が得られるでしょう。しかし、アセンブリ言語ならば高水準言語では調整が難しい細かな命令にまで手が出せます。極限にまで中間言語のサイズを落としたい場合など、特定の要求に効果を発揮するかもしれません。

 計算は、スタックに読み込まれている値を使って行われます。加算演算の場合はadd命令を使います。この命令はスタックから加算する2つの値をポップし、加算した結果をスタックにプッシュします。

add命令
add

 add命令自体は、パラメータを受け取りません。add命令から、他の四則演算の方法も容易に予測できます。減算はsub命令、乗算はmul命令、除算はdiv命令、剰余はrem命令を使います。計算方法は同じで、各演算命令はスタックから計算する値をポップし、結果をプッシュします。

Sample04
.assembly extern mscorlib { }
.assembly test { }

.method static void Main() cil managed {
    .entrypoint
    .maxstack 2
    ldc.i4 1
    ldc.i4 2
    add
    call void [mscorlib]System.Console::WriteLine(int32)

    ldc.i4 5
    ldc.i4 3
    sub
    call void [mscorlib]System.Console::WriteLine(int32)

    ldc.i4 5
    ldc.i4 3
    mul
    call void [mscorlib]System.Console::WriteLine(int32)

    ldc.i4 10
    ldc.i4 2
    div
    call void [mscorlib]System.Console::WriteLine(int32)

    ldc.i4 3
    ldc.i4 2
    rem
    call void [mscorlib]System.Console::WriteLine(int32)

    ret
}
実行結果
3
2
15
5
1

 Sample04は、適当な値をスタックに読み込み、各種演算を行うプログラムです。演算の結果はスタックに戻されるので、それをそのままWriteLine()メソッドで表示しています。このような、整数リテラルを計算するプログラムを高水準言語のコンパイラが生成することはありません。整数リテラルの式はコンパイル時に計算できるため、最適化されてしまいます。実際にC#言語で作成した次のような文を逆アセンブルすると、事前に計算されていることを確認できます。

System.Console.WriteLine(1 + 2);

 上記のコードは、Sample04のようなadd命令を出力することはありません。結果を逆アセンブルすると、整数リテラルが事前に計算され、単純に3という値を読み込んでWriteLine()メソッドで出力するコードに置き換えられます。

 ちなみに、0~8までの値の読み込みにはldc.i4.0~idc.i4.8という専用の命令が割り当てられています。また、-1もldc.i4.m1という命令で読み込めます。これらの命令は、オペコードだけで構成されるため、idc.i4命令よりもサイズが小さく効率的です。通常、-1~8までの定数をスタックに読み込みたい場合は、これら専用の命令を使い、それ以外の値を読み込む場合にldc.i4を使います。

次のページ
制御

この記事は参考になりましたか?

特集記事連載記事一覧

もっと読む

この記事の著者

赤坂 玲音(アカサカ レオン)

平成13年度「全国高校生・専門学校生プログラミングコンテスト 高校生プログラミングの部」にて最優秀賞を受賞。2005 年度~ Microsoft Most Variable Professional Visual Developer - Visual C++。プログラミング入門サイト WisdomSoft の管理人。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/2624 2008/06/26 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー