.NET IL / MSIL 评估堆栈基础
.NET IL / MSIL Evaluation Stack fundamentals
这些问题似乎找不到好的答案。
以下是我认为我知道的和我不清楚的。
- 评估栈是一个类似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
堆栈的元素大小不尽相同,可以包含任意大小的值类型 (struct
s)。
来自 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 个。
来源:
这些问题似乎找不到好的答案。
以下是我认为我知道的和我不清楚的。
- 评估栈是一个类似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
堆栈的元素大小不尽相同,可以包含任意大小的值类型 (struct
s)。
来自 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 integerint32
, a 4-byte signed integernative int
, a signed integer of either 4 or 8 bytes, whichever is more convenient for the target architectureF
, a floating point value (float32
,float64
, or other representation supported by the underlying hardware)&
, a managed pointerO
, 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
指令将整个值类型实例(及其三个数据字段)加载到堆栈上。
感谢
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 个。
来源: