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

时间:2013-11-13 10:06:57

标签: arrays cil

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

Format      Assembly Format     Description
A4 <T>      stelem typeTok      Replace array element at index with the
                                value on the stack

Stack Transition:
…, array, index, value,  ->  …

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

原始规格

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

  • 在说明中:

      

    值的类型必须与指令中的 array-element-compatible-with typeTok

  • 在“正确性”部分:

      

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

  • 在“可验证性”部分:

      
        
    • 跟踪类型的数组T[],部分为T;

    •   
    • 跟踪类型的数组元素兼容 - 与typeTok ;

    •   
    • typeTok array-element-compatible-with T

    •   

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

修改后的规范

然而,只需要存在这样的 typeTok 就等同于 1 将上述规范部分改为

  • 在说明中:

      

    值的类型必须是 array-element-compatible-with array的元素类型

  • 在“正确性”部分中,删除上述部分。

  • 在“可验证性”部分:

      
        
    • 跟踪类型的数组T[],部分为T;

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

    •   

1 如果 value aec-with array 的元素类型,那么因为 aec-with 关系的反身性,的类型或数组的元素类型可以选择为满足要求的 typeTok “原始规格”。相反,如果存在具有给定要求的 typeTok ,则 aec-with 关系的传递性立即产生“修改规范”的要求。 < / p>


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

1 个答案:

答案 0 :(得分:2)

Stelem TypeToken的存在是为了支持非基元的ValueTypes。如果此操作码不存在,唯一的另一种选择是封装这些结构。

有一系列stelem。*元素。对于基元[i,i1,i2,i4,i8,r4,r8和ref]

原始的告诉它期望堆栈上的特定大小的元素并且应该被读取,ref表示存在对象引用。那么struct不是原始的呢?您可能会说只使用相同大小的其中一个原语。毕竟,这就是它对Enum数组的作用。考虑DateTimeOffSet。它是12个字节,因此您无法使用其中一个现有基元。必须使用box来存储这些元素的数组。

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

需要类型信息的

CIL opcodes总是将它们作为操作数,即使它应该基于堆栈上的其他元素显而易见。这可能只是为了让CLR团队的生活更轻松。 (考虑box需要一个操作码)。这也可能有助于排放者避免犯错误。 e.g。

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#通常会抓取struct address元素的地址并存储它们。从技术上讲,你可以使用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>;

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