CIL 的 stelem 指令的 typeTok 参数的目的是什么?



CIL stelem指令(ECMA 335 [pdf]中的III.4.26)指定为

格式
程序集格式说明A4  stelem typeTok索引处的数组元素替换为                                堆栈上的堆栈转换:..., 数组, 索引, 值, -> ...

我不明白typeTok参数的目的是什么。

原始规格

以下是规范中对typeTok的所有提及:

  • 在描述中:

    值的类型必须与指令中的 typeTok 兼容数组元素

  • 在"正确性"部分:

    typeTok 应是有效的typedeftypereftypespec元数据令牌。

  • 在"可验证性"部分中:

    • 对于某些T,数组的跟踪类型是T[];

    • 跟踪的值类型与类型Tok兼容数组元素;

    • typeTok数组元素兼容T

所以typeTok不用于任何东西;它只需要提供。换句话说,我看到的唯一要求是必须存在满足条件的typeTok

修改后的规格

但是,仅仅要求存在这样的typeTok等同于上述规范部分更改为

  • 在描述中:

    值的类型必须与数组元素的元素类型兼容

  • 在"正确性"部分,删除上述部分。

  • 在"可验证性"部分中:

    • 对于某些T,数组的跟踪类型是T[];

    • 跟踪的值类型与数组元素兼容T


1 如果值是 a-e-c-

数组的元素类型,那么由于 a-e-c-with 关系的自反性,可以选择的类型或数组的元素类型作为满足"原始规范"要求的 typeTok。相反,如果存在具有给定要求的typeTok,则a-e-c-with关系的传递性立即产生"修改规范"的要求。


那么我错过了什么?为什么 typeTok 参数在那里(因此为什么除了stelem.ref之外的stelem.<type>指令甚至存在)?

Stelem TypeToken的存在是为了支持非基元的ValueType。如果此操作码不存在,唯一的其他选择是将这些结构框起来。

有一系列石碑*元素。对于基元 [i,i1,i2,i4,i8,r4,r8,和引用]

原始的告诉它期望堆栈上有一个特定大小的元素,它应该被读取,ref 说有一个对象引用。现在,不是原语的struct呢?您可能会说只需使用相同大小的基元之一。毕竟,这就是它对一系列Enum的作用。 考虑DateTimeOffSet .它是 12 个字节,因此您不能使用现有的原语之一。必须装箱来存储这些元素的数组是不好的。

存在的另一个操作码是stelem.any的,它在那里用于通用代码。这只是一个短代码,以防TypeToken可能是对class类型的引用。您始终可以使用stelem.any但如果类型令牌由基元处理,则使用 4 个额外的字节是浪费的。

需要类型信息的CIL opcodes始终将它们作为操作数,即使根据堆栈上的其他元素应该很明显。这可能只是为了让 CLR 团队的生活更轻松。(考虑box需要一个操作码)。这也可能是为了帮助排放者避免犯错误。例如

ldc.i4.8
box typetoken(long)
//whoops we clearly need to conv.i8 before we can box this as a long. 

为什么存在速记版本(例如 Stelem.i4只是stelem typetoken(int32)?它们的存在是少 4 个字节。较短的方法有更好的内联机会。过去,如果一个方法超过 32 个字节的 IL,它就不会被内联。

编辑:我错了。似乎 C# 通常会获取结构地址元素的地址并存储它们。从技术上讲,您可以使用stelem TypeToken(因为这是在通用情况下发出的),但VS团队似乎没有。

var dynmethod = new DynamicMethod("test", typeof(void), new[] { typeof(DateTimeOffset[]), typeof(DateTimeOffset) });
        var gen = dynmethod.GetILGenerator();
        gen.Emit(OpCodes.Ldarg_0);
        gen.Emit(OpCodes.Ldc_I4_0);
        gen.Emit(OpCodes.Ldarg_1);
        gen.Emit(OpCodes.Stelem, typeof(DateTimeOffset));
        gen.Emit(OpCodes.Ret);
        var d=dynmethod.CreateDelegate(typeof(Action<DateTimeOffset[], DateTimeOffset>)) as Action<DateTimeOffset[],DateTimeOffset>;

这个序列按预期工作,所以我不知道他们为什么选择另一条路线。

相关内容

  • 没有找到相关文章

最新更新