似乎无法为这些问题找到一个好的答案。
以下是我认为我知道的以及我所模糊的内容。
以下面的例子为例。这个局部变量是否通过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
堆栈的元素大小不同,可以包含任何大小的值类型(struct
s)。从ECMA-335,第I.12.3.2.1节:
评估堆栈由可以保存任何数据类型的插槽组成,包括值类型的未装箱实例。
[...]
虽然一些JIT编译器可能会更详细地跟踪堆栈上的类型,但CLI仅要求值为以下值之一:
int64
,一个8字节有符号整数int32
,一个4字节有符号整数native int
,一个4或8字节的有符号整数,以更方便的目标架构为准F
,浮点值(float32
,float64
或其他由底层硬件支持的表示)&
,一个管理指针O
,一个对象参考*
,一个“瞬态指针”,只能在单个方法的主体中使用,它指向已知在非托管内存中的值(有关更多详细信息,请参阅CIL指令集规范.*
类型是在内部生成的。 CLI;它们不是由用户创建的)。- 用户定义的值类型
稍早一点,在第I.12.1节:
用户定义的值类型可以出现在内存位置或堆栈中,并且没有大小限制
因此,在您的情况下,ldloc.0
指令将整个值类型实例(带有三个数据字段)加载到堆栈中。
感谢this answer指导我参加这些ECMA部分。那个问题以及该问题的其他答案表明为什么堆栈可以用插槽而不是字节来测量:因为JIT编译器已经在评估如何将MSIL转换为本机指令,所以它必须知道堆栈上值的类型在每一个指令。
如果.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个项目。
资料来源: