3

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

格式 汇编 格式 说明
A4 <T>       stelem typeTok将索引      处的数组元素替换为堆栈上的
                                

堆栈转换: 
……、数组、索引、值、->……

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

原始规格

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

  • 在描述中:

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

  • 在“正确性”部分:

    typeTok 应为有效typedeftyperef、 或typespec元数据令牌。

  • 在“可验证性”部分:

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

    • 跟踪的value类型是array-element-compatible-with typeTok

    • typeTok数组元素兼容 T

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

修改规格

然而,仅仅需要这样一个typeTok的存在就相当于1将上述规范部分更改为

  • 在描述中:

    value的类型必须是array-element-compatible-与array的元素类型

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

  • 在“可验证性”部分:

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

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


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


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

4

1 回答 1

2

Stelem TypeToken存在以支持非原始值类型。如果此操作码不存在,唯一的其他选择是装箱这些结构。

有一系列stelem.* 元素。对于原语 [i,i1,i2,i4,i8,r4,r8,and ref]

原始的告诉它期望堆栈上有一个特定大小的元素并且应该读取它,ref 说有一个对象引用。现在那struct不是原语的呢。您可能会说只使用其中一个相同大小的原语。毕竟,它是为数组所做的Enum。考虑DateTimeOffSet。它是 12 个字节,因此您不能使用现有的原语之一。不得不装箱来存储这些元素的数组是很糟糕的。

存在的另一个操作码是stelem.any通用代码。这只是一个简码,以防TypeToken可能是对class类型的引用。您总是可以使用stelem.any,但如果 typetoken 由原语处理,则使用 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.i4is just 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>;

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

于 2013-11-13T22:11:17.727 回答