Генерация MSIL
Прочие команды MSIL
Упомянем вкратце некоторые интересные команды MSIL, которые редко встречаются в традиционных ассемблерах.
Во-первых, внимания заслуживают специальные команды BOX и UNBOX, реализующие функциональность упаковки и распаковки значений (см. "лекцию 2" ).
Во-вторых, в MSIL предусмотрена специальная команда для создания нового объекта - NEWOBJ. Семантика этой команды такова: создается новый объект и для него вызывается конструктор. Эта операция является критичной для обеспечения целостности данных, так как при ее выполнении гарантируется инициализация объекта (а иначе появляется потенциальная возможность использования "мусорных" ссылочных значений).
В-третьих, отметим, что MSIL содержит специальные команды для обработки исключений ( THROW, RETHROW, ENDFINALLY, ENDFILTER, LEAVE ), что не очень традиционно для низкоуровневых языков.Общая идея реализации исключений заключается в следующем: транслятором создается специальная таблица обработчиков исключений в данном try -блоке; затем при возникновении исключения виртуальная машина .NET просматривает эти таблицы и вызывает соответствующие обработчики. На самом деле, детали реализации исключений не очень существенны, так как при генерации MSIL можно воспользоваться существующими примитивами более высокого уровня (см. ниже про Reflection.Emit ).
Трансляция в MSIL: исходный текст на C-бемоль
Трансляция в MSIL: исходный текст на C 
using System;
class Fib // числа Фибоначчи
{
public static void Main (String[] args)
{
int a = 1, b = 1;
for (int i = 1; i != 10; ++i)
{
Console.WriteLine (a);
int c = a + b;
a = b; b = c;
}
}
}Продемонстрируем трансляцию в MSIL на примере следующей программы, написанной на C
и вычисляющей числа Фибоначчи:
using System;
class Fib
{
public static void Main (String[] args)
{
int a = 1, b = 1;
for (int i = 1; i != 10; ++i)
{
Console.WriteLine (a);
int c = a + b;
a = b; b = c;
}
}
}На следующих слайдах мы покажем результаты трансляции этой программы в MSIL.
Трансляция в MSIL: пример
// объявление имени assembly
.assembly fib as "fib" { /* здесь могут быть параметры */ }.class public Fib {
.method public static void Main () {
.entrypoint // означает начало assembly
.locals (int32 a, int32 b)
ldc.i4.1 // загрузка константы 1
stloc a // сохранение 1 в a (a = 1)
ldc.i4.1
stloc b // аналогично: b = 1
ldc.i4.1 // загрузка 1 на стек (счетчик цикла)
Loop:
ldloc a
call void System.Console::WriteLine(int32)
ldloc a // stack: 1 a
ldloc b // stack: 1 a b
add // stack: 1 (a+b)
ldloc b
stloc a // a = b
stloc b // b = (a+b)
ldc.i4.1
add // инкремент счетчика
dup
ldc.i4.s 10
bne.un.s Loop // сравнение и переход на новую итерацию
pop // удаление счетчика цикла со стека
ret
}
}Программа на MSIL начинается с объявления имени сборки, в которую входит данная программа. Слушателям рекомендуется ознакомиться с более подробным описанием сборки самостоятельно.
Затем объявляется класс Fib , в котором производятся вычисления. Здесь же находится основная точка входа в сборку ( .entrypoint внутри Main ). Затем объявляются локальные переменные; отметим, что в процессе реальной трансляции имена этих переменных будут утеряны.
Наконец, происходит инициализация переменных, подготовка к началу цикла (загрузка счетчика цикла на стек) и выполнение основных вычислений программы: печать очередного числа Фибоначчи, загрузка рабочих переменных на стек, их сложение, присваивание результатов и увеличение счетчика.
Затем происходит сравнение счетчика цикла с максимальным значением цикла и в случае выполнения неравенства " счетчик не равен 10 " происходит переход на начало цикла. По окончании цикла происходит удаление счетчика цикла со стека и выход из метода.