Коды операций CIL

95

Последний аспект CIL-кода связан с ролью, которую играют различные коды операций. Вспомните, что под кодом операции понимается лексема CIL, используемая для построения логики реализации члена. Все поддерживаемые в CIL коды операций (которых немало) могут быть разделены на три основных категории:

В таблице приведены некоторые наиболее полезные коды операций, имеющие непосредственное отношение к логике реализации членов (для удобства они сгруппированы по функциональности):

Коды операций Описание
add, sub, mul, div, rem Позволяют выполнять сложение, вычитание, умножение и деление двух значений (rem возвращает остаток от деления)
and, or, not, xor Позволяют выполнять соответствующие побитовые операции над двумя значениями
ceq, cgt, clt Позволяют сравнивать два значения в стеке различными способами:
ceq — сравнение на предмет равенства;
cgt — сравнение на предмет того, является ли одно из них больше другого;
clt — сравнение на предмет того, является ли одно из них меньше другого
box, unbox Применяются для преобразования ссылочных типов в типы значения и наоборот
ret Применяется для выхода из метода и (если необходимо) возврата значения вызывающему коду
beq, bgt, ble, bit, switch Применяются (вместе с другими похожими кодами операций) для управления логикой ветвления внутри метода:
beq — позволяет переходить к определенной метке в коде, если при проверке значения оказываются равными;
bgt — позволяет переходить к определенной метке в коде, если при проверке одно из значений оказывается больше другого;
ble — позволяет переходить к определенной метке в коде, если при проверке одно из значений оказывается меньше или равным другому;
bit — позволяет переходить к определенной метке в коде, если при проверке одно из значений оказывается меньше другого.
Все связанные с ветвлением коды операций требуют указания в CIL-коде метки, к которой должен осуществляться переход в случае, если результат проверки оказывается истинным
call Применяется для вызова члена определенного типа
newarr, newobj Позволяют размещать в памяти, соответственно, новый массив или новый объект

Коды операций следующей обширной категории применяются для загрузки (заталкивания) аргументов в виртуальный стек выполнения. Обратите внимание, что все эти ориентированные на выполнение загрузки коды операций сопровождаются префиксом ld (который означает "load" — загрузка):

Код операций Описание
ldarg Позволяет загружать в стек аргумент метода. Помимо основного варианта ldarg (который работает с индексом, представляющим аргумент), существует множество других вариантов. Например, есть варианты ldarg, которые работают с числовым суффиксом (ldarg_0) и позволяют жестко кодировать загружаемый аргумент. Также есть варианты, позволяющие жестко кодировать как только один тип данных, к которому относится аргумент, с помощью константной нотации CIL (например, ldarg_I4 для int32), так и тип данных и значение вместе (например, ldarg_I4_5, позволяющий загружать int32 со значением 5)
ldc Позволяет загружать в стек значение константы
ldfld Позволяет загружать в стек значение поля уровня экземпляра
ldloc Позволяет загружать в стек значение локальной переменной
ldobj Позволяет получать все значения размещаемого в куче объекта и помещать их в стек
ldstr Позволяет загружать в стек строковое значение

Помимо кодов операций, связанных с загрузкой, в CIL поддерживаются коды операций, которые позволяют явным образом извлекать из стека самое верхнее значение. Как уже было показано в нескольких примерах ранее, извлечение значения из стека обычно подразумевает его сохранение во временном локальном хранилище с целью дальнейшего использования (например, параметра для последующего вызова метода). Из-за этого многие коды операций, которые позволяют извлекать текущее значение из виртуального стека выполнения, сопровождаются префиксом st (от "store" — сохранить); ниже перечислены некоторые наиболее часто используемые из них:

Код операций Описание
pop Позволяет удалять значение, которое в текущий момент находится на верхушке стека вычислений, но не сохранять его
starg Позволяет сохранять самое верхнее значение из стека в аргументе метода с определенным индексом
stloc Позволяет извлекать текущее значение из верхушки стека вычислений и сохранять его в списке локальных переменных с определенным индексом
stobj Позволяет копировать значение определенного типа из стека вычислений в память по определенному адресу
stsfld Позволяет заменять значение статического поля значением из стека вычислений

Следует принимать во внимание, что различные коды операций в CIL могут предусматривать неявное извлечение значений из стека во время решения поставленной перед ними задачи. Например, при вычитании одного числа из другого с использованием кода операции sub должно быть очевидным, что перед собственно вычислением sub должна извлечь из стека два следующих доступных значения. Результат вычисления будет снова помещен в стек.

Директива .maxstack

При написании кода реализации методов непосредственно в CIL необходимо помнить об одной особой директиве, которая называется .maxstack. Эта директива позволяет указать максимальное количество переменных, которое может помещаться в стек в любой момент во время выполнения метода. Директива имеет значение по умолчанию 8, подходящее для подавляющего большинства создаваемых методов. При желании можно вручную вычислять количество локальных переменных в стеке и указывать его явно.

Пройди тесты
Лучший чат для C# программистов