.NET IL / MSIL 评估堆栈基础

.NET IL / MSIL Evaluation Stack fundamentals

这些问题似乎找不到好的答案。

以下是我认为我知道的和我不清楚的。

举个例子。这个局部变量是否通过 ptr 引用存储在计算堆栈中?

public struct MyStruct
{
    public long x, y, z;

    public static MyStruct Foo()
    {
        MyStruct c;
        c.x = 1;
        c.y = 2;
        c.z = 3;
        return c;   
    }
}

"ldloc.0" 清楚地将结构存储到评估堆栈中,但它也比 64 位大得多。是否存储了引用?

.class public sequential ansi sealed beforefieldinit MyStruct
    extends [mscorlib]System.ValueType
{
    // Fields
    .field public int64 x
    .field public int64 y
    .field public int64 z

    // Methods
    .method public hidebysig static 
        valuetype MyStruct Foo () cil managed 
    {
        // Method begins at RVA 0x2050
        // Code size 34 (0x22)
        .maxstack 2
        .locals init (
            [0] valuetype MyStruct,
            [1] valuetype MyStruct
        )

        IL_0000: nop
        IL_0001: ldloca.s 0
        IL_0003: ldc.i4.1
        IL_0004: conv.i8
        IL_0005: stfld int64 MyStruct::x
        IL_000a: ldloca.s 0
        IL_000c: ldc.i4.2
        IL_000d: conv.i8
        IL_000e: stfld int64 MyStruct::y
        IL_0013: ldloca.s 0
        IL_0015: ldc.i4.3
        IL_0016: conv.i8
        IL_0017: stfld int64 MyStruct::z
        IL_001c: ldloc.0// What is actually stored here?
        IL_001d: stloc.1
        IL_001e: br.s IL_0020

        IL_0020: ldloc.1
        IL_0021: ret
    } // end of method MyStruct::Foo

} // end of class MyStruct

堆栈的元素大小不尽相同,可以包含任意大小的值类型 (structs)。 来自 ECMA-335,第 I.12.3.2.1 节:

The evaluation stack is made up of slots that can hold any data type, including an unboxed instance of a value type.

[...]

While some JIT compilers might track the types on the stack in more detail, the CLI only requires that values be one of:

  • int64, an 8-byte signed integer
  • int32, a 4-byte signed integer
  • native int, a signed integer of either 4 or 8 bytes, whichever is more convenient for the target architecture
  • F, a floating point value (float32, float64, or other representation supported by the underlying hardware)
  • &, a managed pointer
  • O, an object reference
  • *, a “transient pointer,” which can be used only within the body of a single method, that points to a value known to be in unmanaged memory (see the CIL Instruction Set specification for more details. * types are generated internally within the CLI; they are not created by the user).
  • A user-defined value type

早一点,在 I.12.1 节中:

User-defined value types can appear in memory locations or on the stack and have no size limitation

因此,在您的情况下,ldloc.0 指令将整个值类型实例(及其三个数据字段)加载到堆栈上。

感谢 将我指向这些 ECMA 部分。 该问题和该问题的其他答案表明 为什么 堆栈可以用槽而不是字节来衡量:因为 JIT 编译器已经在评估如何将 MSIL 转换为本机指令,所以它有了解每条指令在堆栈上的值的类型。

If .maxsize is 8 does that mean (8 * size_t)?

.maxstack 指令 与运行时计算堆栈的实际大小 无关。相反,它会提示分析工具 同时有多少项驻留在堆栈中。设置 .maxstack 不正确(例如,太小),该方法被认为是不可验证的,这可能会导致低信任场景中的问题(但这对您来说应该不是问题,因为您正在阅读 CIL,不写)。

例如,让我们考虑一个简单的 Add 方法,该方法接受 int 参数,将它们相加,将结果存储在名为 sum 的 class 字段中,然后returns 该字段的值。

.method private hidebysig instance
    int32 Add (
        int32 value1,
        int32 value2
    ) cil managed
{
    .maxstack 3 // At most, there are three elements on the stack.

    ldarg.0                   // 1 item on the stack
    ldarg.1                   // 2 items on the stack
    ldarg.2                   // 3 items on the stack
    add                       // 2 items on the stack
    stfld    int32 Foo::sum   // 0 items on the stack
    ldarg.0                   // 1 item on the stack
    ldfld    int32 Foo::sum   // 1 item on the stack
    ret
}

方法的计算堆栈中同时存在的项目永远不会超过 3 个。


来源:

ECMA-335, Section III.1.7.4