.NET IL / MSIL评估堆栈基础

时间:2019-03-01 21:13:47

标签: .net stack evaluation cil

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

这就是我想知道的和我不清楚的。

  • 评估堆栈是类似于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

2 个答案:

答案 0 :(得分:1)

堆栈的元素大小并非全部相同,并且可以包括任意大小的值类型(struct s)。 摘自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转换为本地指令,所以它必须知道每条指令堆栈上的值的类型。

答案 1 :(得分: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