MSIL 中有一个add
命令可以添加两个参数(从堆栈中弹出、添加、压入堆栈)。它如何知道是否必须弹出 2 个字节或 4 个或 8 个字节?
在 java 中不同的字节码(fadd、dadd、iadd、...),但它们如何在 .NET 中处理呢?
MSIL 中有一个add
命令可以添加两个参数(从堆栈中弹出、添加、压入堆栈)。它如何知道是否必须弹出 2 个字节或 4 个或 8 个字节?
在 java 中不同的字节码(fadd、dadd、iadd、...),但它们如何在 .NET 中处理呢?
Java 字节码被优化为由解释器执行,早期的 JVM 还没有 Hotspot。.NET msil 从一开始就被设计为始终是 jited,不需要针对不同操作数类型的特殊操作码。
抖动从堆栈状态中知道操作数类型。将值压入堆栈的任何操作码也指示类型。说一个 Opcodes.Ldarg_0,jitter 从方法签名中知道类型。跟踪堆栈状态是您永远不想在解释器中做的事情,它会显着减慢代码执行速度,抖动只需执行一次。
来自Common Language Infrastructure (CLI) Partition I:Concepts and Architecture,第 12.1 节:
... CLI 在其对存储在其评估堆栈中的值的操作中仅支持这些类型的一个子集——<code>int32
int64
, 和native int
. 此外,CLI 支持内部数据类型来表示内部评估堆栈上的浮点值。...
如下所述,CIL 指令不指定其操作数类型。相反,CLI 根据数据流跟踪操作数类型,并在下面描述的堆栈一致性要求的帮助下。例如,单
add
条指令将从堆栈中添加两个整数或两个浮点数。
讨论堆栈一致性的“下面”是指第 12.3.2.1 节:
评估堆栈由可以保存任何数据类型的槽组成,包括值类型的未装箱实例。对于所有可能的控制流路径,程序中任何给定点的堆栈类型状态(堆栈深度和堆栈上每个元素的类型)都应相同。例如,将禁止循环未知次数并在每次迭代时将新元素压入堆栈的程序。
也就是说,每当它遇到一条add
指令时,它总是知道该点堆栈的“形状”,因此可以构造正确的本机指令。无论如何,它都在使用一组有限的类型。
(可以从此页面找到 CLI 的其他规范)
如果你写了一个 C# 测试程序并将其反编译为 MSIL,你可以看到大小是由ldc
或调用conv
之前的指令设置的add
。