Dan*_*ker 11 c# java bytecode local-variables
在基于堆栈的中间语言中,例如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;
}
Run Code Online (Sandbox Code Playgroud)
在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
Run Code Online (Sandbox Code Playgroud)
注意加载和存储到四个局部变量.我可以在不使用任何局部变量的情况下编写完全相同的操作(忽略明显的常量传播优化).
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
Run Code Online (Sandbox Code Playgroud)
这似乎对我来说是正确的,而且更短更有效率.那么,为什么基于堆栈的中间语言有局部变量呢?为什么优化编译器如此广泛地使用它们?
根据具体情况,但特别是涉及调用时必须重新排序参数以匹配调用时,如果您没有可供使用的寄存器或变量,则纯栈是不够的.如果你想只做这个堆栈,你需要额外的堆栈操作能力,例如交换/交换堆栈的两个顶级项目的能力.
最后,虽然在这种情况下有可能将所有内容表达为纯栈,但它可能会给代码增加很多复杂性,使其膨胀并使其更难以优化(局部变量是存在的理想候选者)缓存在寄存器中).
还记得在.NET中你可以通过引用传递参数,如何在没有局部变量的情况下为这个方法调用创建IL?
bool TryGet(int key, out string value) {}
Run Code Online (Sandbox Code Playgroud)