在基于堆栈的中间语言(例如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
注意加载并存储到四个局部变量。我可以编写完全相同的操作(不考虑明显的常量传播优化),而无需使用任何局部变量。
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
在我看来,这是正确的,而且更短,更有效。那么,为什么基于堆栈的中间语言具有局部变量呢?为什么优化的编译器会如此广泛地使用它们?
根据情况的不同,尤其是在涉及调用而参数必须重新排序以匹配调用的情况下,如果您没有可使用的寄存器或变量,那么单纯的堆栈是不够的。如果您想只使用此堆栈,则需要附加的堆栈操作能力,例如交换/交换堆栈顶部两个项的功能。
最后,虽然在这种情况下可能将所有内容表示为基于纯堆栈的方法,但它可能会增加代码的整体复杂性,使其膨胀并使其更难以优化(局部变量是实现此目标的理想选择)缓存在寄存器中)。
还要记住,在.NET中,您可以通过引用传递参数,如何在没有局部变量的情况下为该方法调用创建IL?
bool TryGet(int key, out string value) {}
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句