13

CIL や Java バイトコードなどのスタックベースの中間言語にローカル変数があるのはなぜですか? スタックのみを使用できます。手作りの IL にとってはそれほど簡単ではないかもしれませんが、コンパイラーは確実にそれを行うことができます。しかし、私の C# コンパイラはそうではありません。

スタック変数とローカル変数はどちらもメソッドに対してプライベートであり、メソッドが戻ると範囲外になります。したがって、メソッドの外部 (別のスレッド) から見える副作用とは何の関係もありません。

私が正しければ、JIT コンパイラはマシン コードを生成するときにスタック スロットとローカル変数の両方へのロードとストアを排除するため、JIT コンパイラはローカル変数の必要性も認識しません。

一方、C# コンパイラは、最適化を有効にしてコンパイルした場合でも、ローカル変数のロードとストアを生成します。なんで?


たとえば、次の不自然なサンプル コードを見てみましょう。

static int X()
{
    int a = 3;
    int b = 5;
    int c = a + b;
    int d;
    if (c > 5)
        d = 13;
    else
        d = 14;
    c += d;
    return c;
}

最適化を使用して C# でコンパイルすると、以下が生成されます。

    ldc.i4.3        # Load constant int 3
    stloc.0         # Store in local var 0
    ldc.i4.5        # Load constant int 5
    stloc.1         # Store in local var 1
    ldloc.0         # Load from local var 0
    ldloc.1         # Load from local var 1
    add             # Add
    stloc.2         # Store in local var 2
    ldloc.2         # Load from local var 2
    ldc.i4.5        # Load constant int 5
    ble.s label1    # If less than, goto label1
    ldc.i4.s 13     # Load constant int 13
    stloc.3         # Store in local var 3
    br.s label2     # Goto label2
label1:
    ldc.i4.s 14     # Load constant int 14
    stloc.3         # Store in local var 3
label2:
    ldloc.2         # Load from local var 2
    ldloc.3         # Load from local var 3
    add             # Add
    stloc.2         # Store in local var 2
    ldloc.2         # Load from local var 2
    ret             # Return the value

4 つのローカル変数へのロードとストアに注意してください。ローカル変数を使用せずに、まったく同じ操作を (明らかな定数伝播の最適化を無視して) 記述できます。

    ldc.i4.3        # Load constant int 3
    ldc.i4.5        # Load constant int 5
    add             # Add
    dup             # Duplicate top stack element
    ldc.i4.5        # Load constant int 5
    ble.s label1    # If less than, goto label1
    ldc.i4.s 13     # Load constant int 13
    br.s label2     # Goto label2
label1:
    ldc.i4.s 14     # Load constant int 14
label2:
    add             # Add
    ret             # Return the value

それは私には正しいようで、はるかに短く、より効率的です。では、なぜスタックベースの中間言語にはローカル変数があるのでしょうか? そして、なぜ最適化コンパイラはそれらをそれほど広範囲に使用するのでしょうか?

4

2 に答える 2

7

状況にもよりますが、特に呼び出しに合わせてパラメーターを並べ替える必要がある呼び出しが関係している場合、レジスターや変数を自由に使用できない場合、純粋なスタックでは十分ではありません。このスタックのみを作成する場合は、スタックの上位 2 つのアイテムを交換/交換する機能など、追加のスタック操作機能が必要になります。

最終的に、その場合、すべてを純粋なスタックベースとして表現することは可能かもしれませんが、コードが非常に複雑になり、肥大化し、最適化がより困難になる可能性があります (ローカル変数は、レジスタにキャッシュされます)。

また、.NET では参照によってパラメーターを渡すことができることを思い出してください。ローカル変数なしで、このメソッド呼び出し用の IL を作成するにはどうすればよいでしょうか?

bool TryGet(int key, out string value) {}
于 2012-09-16T23:48:59.290 に答える