Какова цель аргумента typeTok для инструкции stelem CIL?
CIL stelem
инструкция (III.4.26 в ECMA 335 [pdf]) определяется как
Формат Сборка Формат Описание A4stelem typeTok Заменить элемент массива в индексе на значение в стеке Переход стека: …, Массив, индекс, значение, -> …
Я не понимаю, какова цель аргумента typeTok.
Оригинальная спецификация
Ниже приведены все упоминания typeTok в спецификации:
В описании:
Тип значения должен быть совместимым с элементом массива с typeTok в инструкции.
В разделе "Корректность":
typeTok должен быть действительным
typedef
,typeref
, или жеtypespec
токен метаданных.В разделе "Проверяемость":
отслеживаемый тип массива
T[]
, для некоторыхT
;отслеживаемый тип значения совместим с элементами массива с typeTok;
typeTok совместим с элементами массива
T
Таким образом, typeTok не используется ни для чего; это просто должно быть обеспечено. Другими словами, единственное требование, которое я вижу, это то, что typeTok, который удовлетворяет условиям, должен существовать.
Модифицированная спецификация
Однако требование простого существования такого typeTok эквивалентно 1 изменению вышеупомянутых разделов спецификации на
В описании:
Тип значения должен быть совместимым с элементом массива с типом элемента массива
В разделе "Корректность" удалите вышеуказанную часть.
В разделе "Проверяемость":
отслеживаемый тип массива
T[]
, для некоторыхT
;отслеживаемый тип значения совместим с элементом массива
T
1 Если значение равно aec-с типом элемента массива, то из-за рефлексивности отношения aec-with можно выбрать либо тип значения, либо тип элемента массива в качестве typeTok, который удовлетворяет требованиям "оригинальной спецификации ". И наоборот, если существует typeTok с заданными требованиями, тогда транзитивность отношения aec-with немедленно приводит к требованиям "Модифицированной спецификации".
Так чего мне не хватает? Почему есть аргумент typeTok (и, следовательно, почему stelem.<type>
инструкции кроме stelem.ref
даже существуют)?
1 ответ
Stelem TypeToken
существует для поддержки ValueTypes, которые не являются примитивами. Единственный другой вариант - заключить в коробку эти структуры, если этот код операции не существует.
Есть семейство стелем.* Элементов. Для примитивов [i,i1,i2,i4,i8,r4,r8 и ref]
примитивные говорят, что он должен ожидать элемент определенного размера в стеке, и он должен быть прочитан, 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# обычно получает адрес элементов структуры 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>;
Эта последовательность работает, как и ожидалось, поэтому я не знаю, почему они выбрали другой маршрут.