Россия |
Генерация 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 " происходит переход на начало цикла. По окончании цикла происходит удаление счетчика цикла со стека и выход из метода.