.NET IL / MSIL评估堆栈基础知识

问题描述 投票:2回答:2

似乎无法为这些问题找到一个好的答案。

以下是我认为我知道的以及我所模糊的内容。

  • 评估堆栈是一个类似C风格堆栈的内存缓冲区(它是一个原生int / size_t的堆栈)?
  • 评估堆栈元素可以是32位或64位(这些是如何在一个堆栈中混合的?)
  • Ldloc_0将局部变量存储在评估堆栈上但是如果它大于64位?
  • Ldloc_0是否只将ptrs存储到评估堆栈上的局部变量中?
  • 存储在评估堆栈中的对象是指针还是原始值?
  • 如果.maxsize为8,那意味着什么(8 * size_t)?如果是这样,如果我阅读说明其32或64位的文档

以下面的例子为例。这个局部变量是否通过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
.net stack evaluation cil
2个回答
1
投票

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

评估堆栈由可以保存任何数据类型的插槽组成,包括值类型的未装箱实例。

[...]

虽然一些JIT编译器可能会更详细地跟踪堆栈上的类型,但CLI仅要求值为以下值之一:

  • int64,一个8字节有符号整数
  • int32,一个4字节有符号整数
  • native int,一个4或8字节的有符号整数,以更方便的目标架构为准
  • F,浮点值(float32float64或其他由底层硬件支持的表示)
  • &,一个管理指针
  • O,一个对象参考
  • *,一个“瞬态指针”,只能在单个方法的主体中使用,它指向已知在非托管内存中的值(有关更多详细信息,请参阅CIL指令集规范.*类型是在内部生成的。 CLI;它们不是由用户创建的)。
  • 用户定义的值类型

稍早一点,在第I.12.1节:

用户定义的值类型可以出现在内存位置或堆栈中,并且没有大小限制

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

感谢this answer指导我参加这些ECMA部分。那个问题以及该问题的其他答案表明为什么堆栈可以用插槽而不是字节来测量:因为JIT编译器已经在评估如何将MSIL转换为本机指令,所以它必须知道堆栈上值的类型在每一个指令。


0
投票

如果.maxsize为8,那意味着什么(8 * size_t)?

.maxstack指令与运行时评估堆栈的实际大小无关。相反,它暗示了分析工具同时有多少项驻留在堆栈上。错误地设置.maxstack(因为太小),该方法被认为是不可验证的,这可能导致低信任情况下的问题(但这对您来说不应该是一个问题,因为您正在阅读CIL,而不是写作)。

例如,让我们考虑一个简单的Add方法,它接受int参数,将它们加在一起,将结果存储在一个名为sum的类字段中,并返回该字段的值。

.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

© www.soinside.com 2019 - 2024. All rights reserved.