有局部變量在基於堆棧的中間語言,如CIL或Java字節碼,爲什麼會有局部變量?只能使用堆棧。手工製作的IL可能不那麼容易,但編譯器肯定可以做到。但我的C#編譯器沒有。爲什麼是基於堆棧的字節碼IL
堆棧和局部變量都是方法專用的,並在方法返回時超出範圍。所以它不可能與方法外部可見的副作用(來自另一個線程)有關。
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
注意加載和存儲的四個局部變量。我可以在不使用任何局部變量的情況下編寫完全相同的操作(不考慮明顯的常量傳播優化)。
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
這對我來說似乎是正確的,而且更短,更高效。那麼,爲什麼基於堆棧的中間語言有局部變量呢?爲什麼優化編譯器如此廣泛地使用它們?
您不能*總是*做出如您在示例中演示的簡單轉換。 –
這個問題問爲什麼「命名的插槽」是*需要*或爲什麼C#「優化」的輸出看起來過於冗長(例如*在這種情況下使用*)? – 2012-09-17 00:02:06
即使在某些情況下(這種情況?)需要或使用「命名空位」,爲什麼優化編譯器不能消除大多數加載和存儲?這似乎很微不足道。我肯定錯過了什麼。 – Virtlink