有局部变量在基于堆栈的中间语言,如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