为什么基于堆栈的 IL 字节码中存在局部变量
在基于堆栈的中间语言(如 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
这对我来说似乎是正确的,而且更短,更有效率。那么,为什么基于堆栈的中间语言有局部变量呢?为什么优化编译器如此广泛地使用它们?