bucket-sort logo bucket-sort

プログラミングとインフラエンジニアリングの覚え書き

  • Posts
  • About
  • Contact
  1. Home
  2. All Posts
  3. [C#] CIL の命令とスタック動作を読み解く

[C#] CIL の命令とスタック動作を読み解く

Jul 13, 2026 C# , .NET bucket-sort

前回は、CIL で型やメンバーがどう表現されるかを見ました。

今回は、CIL の命令をもう少し詳しく読みます。
特に、評価スタック、ローカル変数、引数、this 参照、分岐、ループに注目します。

命令を読む前に

CIL はスタックベースです。
多くの命令は、評価スタックへ値を積む、評価スタックから値を取り出す、結果を再び積む、という動きをします。

代表的な命令をいくつか見ておきます。

値を積む命令

ldc.i4.0
ldc.i4.1
ldc.i4.s 10
ldstr "hello"
ldarg.0
ldloc.0

整数、文字列、引数、ローカル変数などを評価スタックへ積みます。

値を保存する命令

stloc.0
stfld int32 Sample::count

評価スタック上の値を、ローカル変数やフィールドへ保存します。

計算や比較を行う命令

add
sub
mul
div
rem
ceq
cgt
clt

スタック上の値を取り出して計算や比較を行い、結果をスタックへ戻します。

分岐する命令

br.s LABEL
brtrue.s LABEL
brfalse.s LABEL

ラベルへジャンプします。
C# の if や while は、CIL では分岐命令とラベルで表現されます。

最大スタックサイズ

CIL のメソッドには、評価スタックが最大でどれくらい必要かを示す指定があります。

.maxstack 2

これは「このメソッドでは、評価スタックに最大 2 個の値が同時に積まれる」という目安です。

たとえば、次の C# コードを考えます。

public static int Add(int x, int y)
{
    return x + y;
}

概念的な CIL です。

.maxstack 2
ldarg.0
ldarg.1
add
ret

ldarg.0 と ldarg.1 で 2 つの値が積まれるため、最大スタックサイズは 2 で足ります。

ローカル変数

C# のローカル変数は、CIL では .locals で宣言されます。

public static int AddWithLocal(int x, int y)
{
    int result = x + y;
    return result;
}

概念的な CIL です。

.locals init (
  [0] int32 result
)

ldarg.0
ldarg.1
add
stloc.0
ldloc.0
ret

流れは次の通りです。

x を積む
y を積む
加算する
結果をローカル変数 0 番へ保存する
ローカル変数 0 番を積む
戻り値として返す

C# では変数名で扱いますが、CIL ではインデックスで扱われます。

引数とローカル変数の対応

引数は ldarg 系の命令で読み込みます。

ldarg.0
ldarg.1
ldarg.2

静的メソッドでは、ldarg.0 が 1 番目の引数です。
インスタンスメソッドでは、ldarg.0 は this を表し、実際の第 1 引数は ldarg.1 になります。

public class Counter
{
    private int value;

    public void Add(int delta)
    {
        value += delta;
    }
}

概念的な CIL です。

ldarg.0
ldarg.0
ldfld int32 Counter::value
ldarg.1
add
stfld int32 Counter::value
ret

ここで ldarg.0 は this、ldarg.1 は delta です。

隠れた this 参照

インスタンスメソッドには、C# の引数一覧に見えない this が暗黙的に渡されます。

public string GetName()
{
    return Name;
}

概念的な CIL です。

ldarg.0
call instance string Person::get_Name()
ret

ldarg.0 で現在のインスタンスを積み、それに対してプロパティ取得メソッドを呼び出しています。

この見方ができると、インスタンスメソッドと静的メソッドの違いがかなり明確になります。

条件分岐

C# の if 文です。

public static string Check(int value)
{
    if (value > 0)
    {
        return "positive";
    }

    return "zero or negative";
}

概念的な CIL です。

ldarg.0
ldc.i4.0
cgt
brfalse.s ELSE_LABEL

ldstr "positive"
ret

ELSE_LABEL:
ldstr "zero or negative"
ret

cgt は「より大きいか」を比較し、結果をスタックへ積みます。
brfalse.s は、スタック上の値が false 相当なら指定ラベルへジャンプします。

ループ

C# の for 文です。

public static int SumTo(int max)
{
    int total = 0;

    for (int i = 1; i <= max; i++)
    {
        total += i;
    }

    return total;
}

概念的な CIL は、ラベルと分岐で表現されます。

.locals init (
  [0] int32 total,
  [1] int32 i
)

ldc.i4.0
stloc.0

ldc.i4.1
stloc.1

br.s CHECK

LOOP:
ldloc.0
ldloc.1
add
stloc.0

ldloc.1
ldc.i4.1
add
stloc.1

CHECK:
ldloc.1
ldarg.0
ble.s LOOP

ldloc.0
ret

C# のループ構文は、CIL では「初期化」「条件確認」「本体」「更新」「分岐」の組み合わせになります。

ラベルの役割

ラベルは、分岐命令のジャンプ先です。

br.s CHECK

LOOP:
// ループ本体

CHECK:
// 条件確認

C# の if、for、while、switch などは、CIL 上ではラベルと分岐命令に変換されます。

高水準の構文は違って見えても、低水準では「どこへジャンプするか」が重要になります。

CIL を読むときのコツ

CIL を読むときは、すべての命令を丸暗記しようとしなくて大丈夫です。
まずは次の順番で追うと読みやすくなります。

  1. メソッドの引数と戻り値を見る
  2. ローカル変数を見る
  3. スタックへ値を積む命令を見る
  4. 計算や比較の命令を見る
  5. 分岐先のラベルを見る
  6. ret までの流れを追う

特に、スタック上に今どの値が積まれているかを頭の中で追えるようになると、CIL の見通しがよくなります。

まとめ

CIL の命令は、評価スタックを中心に動きます。
引数やローカル変数をスタックへ積み、計算し、結果を保存したり返したりします。

また、C# の制御構文は、CIL ではラベルと分岐命令として表現されます。
次回は、CIL を手で書くのではなく、実行時にアセンブリを生成する System.Reflection.Emit の世界を見ていきます。

C# .NET CIL IL オペコード スタック 分岐 ループ
← [C#] CIL で型とメンバーがどう表現されるか [C#] 実行時にアセンブリを生成する →

Related Posts

  • [C#] CIL で型とメンバーがどう表現されるか Jul 12, 2026
  • [C#] CIL を読む理由と基本文法を理解する Jul 11, 2026
  • [C#] 実行時にアセンブリを生成する Jul 14, 2026
  • [C#] .NET アセンブリの構造とライブラリの種類を理解する Jul 1, 2026

Table of Contents

  • 命令を読む前に
    • 値を積む命令
    • 値を保存する命令
    • 計算や比較を行う命令
    • 分岐する命令
  • 最大スタックサイズ
  • ローカル変数
  • 引数とローカル変数の対応
  • 隠れた this 参照
  • 条件分岐
  • ループ
  • ラベルの役割
  • CIL を読むときのコツ
  • まとめ

Recent Posts

  • [C#] EF Core でデータベースの準備と初期データを扱う Aug 8, 2026
  • [C#] EF Core とリポジトリでデータ操作を整理する Aug 7, 2026
  • [C#] DbContext の設定と保存処理を拡張する Aug 6, 2026
  • [C#] EF Core のエンティティと表示用モデルを設計する Aug 5, 2026
  • [C#] EF Core を中心にデータアクセス層を分ける Aug 4, 2026

Categories

  • C#150
  • .NET149
  • AWS27
  • Laravel16
  • Entity Framework Core15
  • Linux15
  • MySQL9
  • Apache8
  • PHP8
  • Data Access6
  • DynamoDB6
  • セキュリティ6
  • Nginx5
  • WordPress4
  • インフラ4
  • Hugo3
  • .NET Framework1
  • Aurora1
  • Diagnostics1
  • Filament1

Tags

  • C#
  • .NET
  • AWS
  • Laravel
  • コレクション
  • PHP
  • Entity Framework Core
  • セキュリティ
  • MySQL
  • Linux
  • パフォーマンス
  • Apache
  • LINQ
  • System.Collections.Generic
  • デリゲート
  • リフレクション
  • ADO.NET
  • Code Snippet
  • DynamoDB
  • NoSQL
  • PHP-FPM
  • RDS
  • System.Collections
  • Windows
  • メタデータ
  • メモリ管理
  • CIL
  • DoS
  • Nginx
  • SQL Server
  • WordPress
  • ラムダ式
  • 監視
  • 設計
  • Amazon Linux 2023
  • Delegate
  • Docker
  • IDisposable
  • Ipset
  • Iptables
  • LINQ to Objects
  • OPCache
  • Pointer
  • Reflection
  • System.Collections.Specialized
  • Unsafe
  • Webサーバー
  • アセンブリ
  • インターフェース
  • オブジェクト指向
Powered by Hugo & Explore Theme.