为什么基于堆栈的IL字节码中存在局部变量

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)

这似乎对我来说是正确的,而且更短更有效率.那么,为什么基于堆栈的中间语言有局部变量呢?为什么优化编译器如此广泛地使用它们?

Luc*_*ero 7

根据具体情况,但特别是涉及调用时必须重新排序参数以匹配调用时,如果您没有可供使用的寄存器或变量,则纯栈是不够的.如果你想只做这个堆栈,你需要额外的堆栈操作能力,例如交换/交换堆栈的两个顶级项目的能力.

最后,虽然在这种情况下有可能将所有内容表达为纯栈,但它可能会给代码增加很多复杂性,使其膨胀并使其更难以优化(局部变量是存在的理想候选者)缓存在寄存器中).

还记得在.NET中你可以通过引用传递参数,如何在没有局部变量的情况下为这个方法调用创建IL?

bool TryGet(int key, out string value) {}
Run Code Online (Sandbox Code Playgroud)