Компания планирует поставлять Aries со всеми такими системами, поэтому пользователи смогут устанавливать и запускать приложения так, как если бы они работали на «родной» платформе PA-RISC.

Переход на новую архитектуру никогда не был простой задачей. Пользователи хотят, чтобы их любимые приложения работали как обычно и не пришлось тратить время на их адаптацию к иной платформе. Для некоторых унаследованных приложений ситуация складывается еще хуже. При отсутствии полных исходных текстов перекомпилировать приложение для новой платформы просто невозможно. Таким образом, перенос этих унаследованных приложений не просто будет происходить медленно, он станет попросту невозможен. Бинарная трансляция помогает в этом процессе переноса, поскольку она автоматизирует преобразование двоичного кода из одного набора команд в другой при отсутствии исходного текста, написанного на языке высокого уровня. Существует множество видов бинарной трансляции, но пользователям обычно приходится выбирать между некоторой формой интерпретации (или эмуляции) и статической трансляцией. Интерпретация не требует вмешательства пользователя, но производительность оказывается очень невелика. Статическая трансляция, с другой стороны, требует определенных усилий со стороны пользователя, но обеспечивает намного более высокую производительность.

Чтобы помочь пользователям PA-RISC перейти на новые системы в архитектуре IA-64 компания Hewlett-Packard разработала программный эмулятор Aries. В момент подготовки этой статьи к печати Aries был единственным программным обеспечением на базе IA-64, служащим для переноса программ. Его уникальность еще и в том, что этот эмулятор объединяет быструю интерпретацию и динамическую трансляцию. Используя свой быстрый интерпретатор Aries точно эмулирует весь набор команд PA-RISC без вмешательства пользователя. Во время интерпретации он отслеживает шаблон выполнения приложения в момент выполнения программы и транслирует в «родной» код IA-64 только часто исполняемый код. В конце эмуляции Aries удаляет весь оттранслированный код, оставляя оригинальное приложение без изменений. Таким образом, динамическая трансляция обеспечивает быструю эмуляцию и сохраняет целостность эмулируемого приложения PA-RISC.

Благодаря этому сочетанию быстрой интерпретации и динамической трансляции пользователи могут рассчитывать на то, что приложения PA-RISC будут прозрачно, точно и эффективно выполняться на любом компьютере в архитектуре IA-64 с операционной системой HP-UX. Компания HP планирует поставлять Aries на всех подобных системах, поэтому пользователи смогут устанавливать и запускать приложения так, как если бы они работали на «родной» платформе PA-RISC.

Это сочетание также позволяет эмулировать архитектуру набора команд (ISA) с меньшими затратами и при более высокой производительности, чем другие подходы к эмуляции. Быстрый интерпретатор эмулирует блоки команд, которые редко выполняются, в то время как динамический транслятор увеличивает производительность эмуляции за счет трансляции часто выполняемых блоков команд в «родной» код IA-64. Для приложений общего назначения, которые тратят большую часть своего времени на небольшие фрагменты кода, затраты на эмуляцию окажутся значительно ниже, поскольку Aries транслирует в «родные» команды IA-64 только наиболее часто выполняемый код. В то же самое время, Aries увеличивает производительность, поскольку транслируемый код выполняется значительно быстрее, чем эмулированный.

Ресурсы в архитектуре IA-64 также позволяют легко транслировать команды PA-RISC. Архитектура IA-64 имеет 128 регистров общего назначения и 128 регистров для операций с числами с плавающей запятой. У архитектуры PA-RISC и тех, и других регистров по 32. Aries может использовать дополнительные регистры IA-64 для того, чтобы ликвидировать ограничения на ресурсы, которые имеются в оригинальных исполняемых модулях PA-RISC. Кроме того, IA-64 содержит больше устройств для параллельного исполнения, чем PA-RISC, в силу чего эмулятор может более эффективно использовать параллелизм на уровне команд (ILP).

Наконец, информация времени исполнения помогает динамическому транслятору создавать более эффективный код, поскольку в этом случае он лучше представляет, как ведет себя программа во время выполнения. Например, если известно, что в 95% случаях косвенный переход приводит к той же самой цели, транслятор может сгенерировать прямой переход к этой цели вместе с общим косвенным переходом. Таким образом, он превращает трудно предсказуемый косвенный переход в переход с предсказуемостью 95%. Это особенно важно для сокращения накладных расходов на предсказание косвенного перехода, которые обычно весьма высоки при выполнении приложения.

Механизм работы

На рис. 1 показаны пять основных компонентов Aries: модуль начальной загрузки, быстрый интерпретатор, динамический транслятор, модуль эмуляции среды и модуль времени исполнения Aries. Aries увеличивает производительность эмуляции, поскольку транслирует код во время исполнения и интерпретирует только те блоки команд, которые выполняются заданное число раз. Когда пользователь выполняет приложение любого типа в системе IA-64, ядро HP-UX проверяет заголовок приложения с тем, чтобы выяснить, для какой архитектуры создавался исполняемый код - для IA-64 или для PA-RISC. Если обнаруживается, что исполняемый код создавался для IA-64, то ядро HP-UX начинает нормальный процесс выполнения. Если же исполняемый код является «родным» для PA-RISC, ядро устанавливает соответствие текста и сегментов данных исполняемого кода, загружает модуль начальной загрузки Aries и передает ему управление. Модуль загрузки затем загружает другие компоненты Aries и передает управление модулю времени исполнения Aries, который, в свою очередь, вызывает интерпретатор, чтобы начать реальную эмуляцию.

Рис. 1. Пять основных компонентов Aries.

Модуль времени исполнения управляет большей частью процесса эмуляции, поэтому пользователю ничего не приходиться делать в автономном режиме. Модуль начальной загрузки выполняет только одну функцию: он вызывает модуль времени исполнения Aries в ответ на запрос ядра операционной системы HP-UX

Модуль времени исполнения отслеживает, насколько часто исполняется блок. Если блок исполняется фиксированное число раз (соответствует порогу трансляции), модуль времени исполнения вызывает динамический транслятор, который транслирует блок PA-RISC в блок «родных» команд IA-64, функционально эквивалентных данному блоку PA-RISC. Эти команды называются динкодом (dyncode). Для каждой последующей эмуляции данного блока PA-RISC, который теперь становится «горячим блоком», модуль времени исполнения Aries будет вместо интерпретатора вызывать динкод горячего блока.

Помимо использования быстрой интерпретации и динамической трансляции, Aries устанавливает соответствие регистров PA-RISC «родным» регистрам IA-64 тогда, когда это возможно, для того, чтобы увеличить производительность эмуляции. Когда Aries выполняет динкод, он устанавливает соответствие всех регистров PA-RISC общего назначения выделенным регистрам IA-64 для быстрой ссылки. Когда Aries выполняет интерпретацию, он отображает эти регистры в память так, что для работы с ними можно легко использовать язык высокого уровня. Когда Aries переключается обратно на выполнение динкода, он загружает регистры PA-RISC общего назначения в определенные регистры IA-64. Когда он опять переходит в режим интерпретации, Aries переносит регистры PA-RISC обратно в память.

По мере того, как Aries транслирует в динкод все больше блоков PA-RISC, он тратит на динкод все больше времени и, тем самым все реже переключает режимы. С другой стороны, Aries отображает регистры PA-RISC с плавающей точкой в память во время эмуляции, поскольку регистры с для чисел с плавающей запятой с одинарной точностью в архитектуре PA-RISC спарены.

Приложение PA-RISC во время исполнения может обратиться к операционной системе за системной функцией, такой как открытие файла. Когда Aries эмулирует такое приложение, он вызывает модуль эмуляции среды для обработки подобных запросов к системным функциям, которые в основном представляют собой системные вызовы и сигналы, эмулируя их поведение на платформе IA-64. Поскольку на платформе IA-64 также работает операционная система HP-UX, то Aries может отображать большинство системных функций PA-RISC/HP-UX непосредственно на соответствующие системные функции IA-64/HP-UX.

Модули начальной загрузки и времени исполнения

Модуль начальной загрузки на самом деле не является составляющей реального процесса эмуляции; он лишь загружает другие компоненты Aries и передает управление модулю времени исполнения Aries, когда ядро HP-UX определяет наличие приложения PA-RISC. И Aries, и модуль начальной загрузки Aries созданы как разделяемые библиотеки для того, чтобы во время эмуляции сохранить исходное приложение PA-RISC без изменений. Таким образом, когда пользователи видят исполняемый процесс, они видят оригинальный процесс PA-RISC, а не процесс Aries.

Модуль времени исполнения - это концентратор Aries. Он отвечает за контроль управляющей логики в выполняемом процессе эмуляции. Он отслеживает, сколько раз исполнялся блок и принимает решение, когда вызывать динамический транслятор для трансляции блока PA-RISC. Он также определяет, когда вызывать модуль эмуляции среды для обработки запросов на системную функцию.

Интерпретатор

Рис. 3. «Плохой блок» команд в Aries.

Этот блок заканчивается обнуленным обратным переходом со слотом задержки (команда, которая следует за переходом), которая также является переходом. Выполнение этого базового блока может породить три возможные последовательности выполнения. Когда оба перехода (B1 и B2) выбраны, как в случае 1, последовательность выполнения переходит сразу от команды 0x1000 к команде 0x3000 без выполнения заканчивающей блок команды перехода для блока, начинающегося с команды 0x1000. Это нарушает правила трансляции Aries. Интерпретатор Aries отвечает за идентификацию этих блоков, в силу чего Aries никогда не будет пытаться их транслировать. Однако такого рода «плохие блоки» крайне редко встречаются в нормальных приложениях PA-RISC

Рис. 2 показывает детальный путь эмуляции команды. Быстрый интерпретатор - это эмуляция сети безопасности, поскольку он обрабатывает все возможные блоки, в том числе те, которые еще не транслировались или которые слишком сложны для трансляции. Определенным командам PA-RISC нельзя сопоставить команды IA-64, которые были бы им функционально эквивалентны. Такие команды намного разумнее интерпретировать, нежели транслировать в «родные» команды IA-64. Например, регистр состояния для чисел с плавающей запятой (FPSR) отображается на FR0 в архитектуре PA-RISC. Для работы со значением в FPSR приложение использует команды загрузки или сохранения, которые работают с регистром FR0. Aries не может преобразовывать такую команду PA-RISC в простую последовательность команд IA-64, поскольку он не отображает эмулированный PA-RISC FPSR полностью на «родной» регистр IA-64 FPSR во время эмуляции. Таким образом, Aries интерпретирует любой базовый блок PA-RISC, содержащих такого рода команды, а не транслирует его.

Интерпретатор также должен собирать профильную информацию времени исполнения, такую как адреса переходов. Модуль времени исполнения Aries использует эту информацию позже для того, чтобы помочь выбирать и транслировать горячие блоки (блоки, которые соответствуют пороговым значениям трансляции).

Интерпретатор также указывает блоки, которые трудно транслировать, как показано на Рисунке 3, маркируя их, как «плохие блоки», которые никогда не будут переданы динамическому транслятору.

Динамический транслятор

Динамический транслятор транслирует базовый блок команд PA-RISC в динкод и сохраняет оттранслированный код в кэше кода Aries для последующего использования. Он имеет четыре подкомпоненты.

Процессор PA-RISC. Процессор PA-RISC сканирует каждую команду PA-RISC в блоке и записывает полезную информацию для последующей генерации кода. Он также выполняет некоторую оптимизацию, предваряющую трансляцию, специфическую для архитектуры PA-RISC. К примеру, большинство арифметических команд PA-RISC генерирует биты переноса/заимствования, которые редко используются. Чтобы минимизировать избыточную генерацию битов переноса/заимствования, препроцессор отслеживает информацию о том, где определен ресурс (такой, как регистр) и где он будет использоваться в последовательности исполнения. Таким образом, генератор кода порождает только необходимые биты переноса/заимствования. Препроцессор также выполняет тиражирование констант и копий, чтобы сократить зависимости между командами PA-RISC так, что планировщик может реализовать больший уровень ILP.

? Генератор кода. Генератор кода транслирует препроцессированные команды PA-RISC в «родные» команды IA-64. Поскольку Aries отображает все регистры PA-RISC общего назначения на определенные регистры IA-64 в динкоде, генератор кода может использовать соответствующие регистры IA-64 для того, чтобы напрямую ссылаться на регистры PA-RISC общего назначения. Это позволяет обойтись без необходимости выбирать значения регистров из памяти. Генератор кода также устраняет любые различия режима между процессами PA-RISC и IA-64. Например, если Aries эмулирует 32-разрядное приложение PA-RISC, он должен выравнивать адресные ссылки до 64 разрядов. Для каждой команды PA-RISC, предусматривающей обращение к памяти, генератор кода должен генерировать дополнительную команду IA-64 - addp4, для того, чтобы выполнить преобразование перед генерацией команды загрузки или сохранения. Этот процесс называется дополнением адресов (address swizzling).

? Оптимизатор и планировщик. Генератор кода передает команды IA-64 упрощенному оптимизатору для оптимизации и планирования выполнения кода. Поскольку оптимизация осуществляется во время выполнения, она должна быть быстрой и эффективной. Aries использует несколько методик для осуществления эффективной оптимизации, в том числе, помимо других, следующие.

? Удаление неиспользуемого кода. Aries удаляет избыточные команды с тем, чтобы сократить размер окончательного оттранслированного кода.

? Сокращение дополнения адресов. Aries заменяет определенные пары команд addp4/загрузить или addp4/сохранить на одну команду загрузки или сохранения, чтобы сократить общий размер кода и общее число циклов выполнения. На рис. 4 показано, как оптимизатор использует сокращение дополнения адресов для оптимизации последовательности подряд идущих команд обращениz к памяти.

? Сокращение числа операций выравнивания памяти. Aries отличает команды обращения к памяти, которые он генерирует для выбора регистров, от нормальных команд работы с памятью, которые он генерирует для эмулируемого приложения PA-RISC. Эти два типа команд работы с памятью обращаются к различным сегментам памяти и не перекрываются. Aries может безопасно переносить команды работы с памятью одного типа за команды другого типа с тем, чтобы добиться более высокого уровня ILP.

Планировщик Aries группирует команды для каждого генерируемого блока IA-64 так, что все команды соответствуют шаблонам IA-64. Он начинает с построения направленного ациклического графа (directed acyclic graph, DAG), чтобы собрать все зависимости, специфические для IA-64 и для микроархитектуры, такие как зависимости между командами типа запись-после-чтения (WAR), чтение-после-записи (RAW) и запись-после-записи (WAW). На основе DAG он выбирает команды, которые можно свободно планировать в каждом цикле. Наконец, планировщик использует конечный автомат для группировки команды в каждом цикле, добавляя там, где необходимо NOP (отсутствие операций). Он также использует эвристику для сокращения числа вставляемых NOP.

? Упаковщик команд. Упаковщик команд упаковывает запланированные команды IA-64 в бинарный код и записывает его в кэш кода Aries. Модуль времени исполнения Aries затем обновляет таблицу отображения адресов для того, чтобы отразить изменение состояния для оттранслированного блока PA-RISC. При последующих эмуляциях данного блока модуль времени исполнения Aries будет использовать оттранслированный код вместо того, чтобы вызывать интерпретатор.

Aries реализует методику, которая позволяет блоку динкода непосредственно переходить к другому блоку динкода без обращения к таблице отображения адресов, тем самым добиваясь большей эффективности при переходе между блоками. Модуль времени исполнения Aries отслеживает блок динкода, который выполняется. Если следующий блок, который будет выполняться - это адресуемый блок предыдущего блока, Aries модифицирует конечную команду перехода в предыдущем исполняемом блоке так, что он может перейти непосредственно к адресуемому блоку динкода.

Aries может также преобразовать динамически сгенерированный код и самомодифицируемый код в эмулированном приложении PA-RISC, трактуя оба типа кода как обычные блоки PA-RISC в эмулированном приложении. Когда Aries встречает команду sync, которая указывает на наличие самомодифицируемого кода, он просто очищает текущее содержимое кэша кода так, что при последующих эмуляциях не будут использоваться никакие трансляции старого кода.

Модуль эмуляции среды

Наиболее общие системные функции, которые модуль эмуляции среды должны обрабатывать, - это системные вызовы и доставка сигнала.

? Системные вызовы. Все системные вызовы HP-UX попадают в пространство ядра через шлюзовую страницу системных вызовов. Модуль эмуляции среды получает системные вызовы, сделанные в эмулированном приложении PA-RISC на шлюзовой странице и вызывает соответствующие утилиты эмуляции. Большинство утилит эмуляции системных вызовов - это простые «заглушки», которые инициируют «родные» системные вызовы непосредственно на платформе IA-64/HP-UX. Другие системные вызовы требуют специальной обработки перед выполнением системных вызовов. Например, когда нить в многонитевом приложении PA-RISC обращается к операционной системе с запросом приостановить другой поток, Aries не может просто передать этот запрос базовому ядру IA-64, поскольку это может вызвать блокировку при доступе к разделяемым ресурсам Aries. Таким образом, Aries должен получить все разделяемые ресурсы Aries прежде, чем передать ядру «родной» запрос на приостановку.

? Доставка сигнала. Доставка сигналов также требует специальной обработки. Операционная система может доставлять приложению PA-RISC как синхронные, так и асинхронные сигналы. Она доставляет приложению синхронный сигнал, такой как нарушение защиты при загрузке, сразу при выполнении команды, которая вызвала исключение. С другой стороны, асинхронный сигнал, такой как kill или suspend, не связан с конкретной командой, поэтому система может доставлять его приложению в любое время, и время, когда он появляется, может отличаться от запуска к запуску.

Aries регистрирует главный обработчик сигнала (master signal handler) для обработки доставки эмулируемому приложению PA-RISC всех сигналов, которые он получает, как синхронных, так и асинхронных. Когда ядро HP-UX выявляет исключение, которое генерирует приложение PA-RISC, он доставляет сигнал процессу Aries, эмулирующему приложение PA-RISC посредством вызова главного обработчика сигнала. Обработчик сигнала затем определяет, как сигнал должен быть доставлен эмулируемому приложению. Aries не всегда доставляет асинхронные сигналы сразу же, как только они возникают. Вместо этого он устанавливает очередь получаемых асинхронных сигналов и доставляет их эмулируемому приложению тогда, когда он может создать корректный контекст сигналов PA-RISC для эмулируемого приложения. Синхронный сигнал Aries передает сразу же. Когда Aries получает синхронный сигнал в динкоде, где контекст сигналов PA-RISC может не соответствовать текущему положению вещей, Aries создает блок восстановления для динкода. Затем он выполняет этот блок восстановления для синхронизации контекста PA-RISC перед доставкой сигнала эмулируемому приложению.

Проверка эмуляции и трансляции

Одна из наиболее важных задач, стоящих перед Aries, состоит в том, что он эмулирует все приложения PA-RISC пользовательского уровня на платформах IA-64, в том числе приложения, разработка которых еще не завершена, так что даже запустить все имеющиеся приложения PA-RISC на Aries, чтобы проверить корректность их эмуляции, невозможно. Более того, большинство существующих приложений PA-RISC сгенерированы компилятором. Поскольку компиляторы используют только подмножество ISA для генерации исполняемого кода, добиться стопроцентной эмуляции ISA при тестировании приложений очень сложно. В силу чего мы выбрали следующие способы проверки Aries.

Мы разработали оболочку случайного тестирования для тщательной проверки корректности эмуляции ISA с помощью Aries. Мы использовали эту оболочку для случайно генерированных последовательностей команд PA-RISC и затем выполняли каждую последовательность команд дважды: один раз на процессоре PA-RISC и один раз с помощью Aries в системе IA-64. Мы сравнивали финальные состояния и отмечали все несоответствия между ними как ошибки эмуляции Aries. С помощью этой оболочки мы тщательно проверили все эмуляции ISA, в том числе сценарии, которые могут никогда не появится в реальном приложении.

Мы также интегрировали в Aries механизм перекрестной проверки времени исполнения, который позволял определить точное местонахождение ошибки эмуляции. Это важно при работе с большими и сложными приложениями, поскольку ошибка может не проявляться в распознаваемом формате (таком, как вывод) в течение определенного времени после ее возникновении. Мы можем идентифицировать блок команд, когда возникает ошибка эмуляции. С помощью такого механизма Aries может запускать любое большое и сложное приложение и сообщать, где точно возникла ошибка эмуляции без вмешательства пользователя.

С другой стороны, проверка корректности трансляции была очень сложной, поскольку, когда мы разрабатывали Aries реальной системы IA-64 не существовало. Чтобы решить эту задачу мы добавили эмулятор команд IA-64 в Aries, который действовал как механизм, где выполнялся динкод. Мы создали остальные компоненты эмуляции Aries - интерпретатор, динамический транслятор, модуль эмуляции среды и модуль времени исполнения, как приложения PA-RISC и запускали их на платформе PA-RISC. Такой подход к тестированию позволил увеличить эффективность тестирования динкода в 300 раз по сравнению с традиционным методом проверки, предусматривающим использование полнофункциональной модели IA-64.

Aries может эмулировать большинство приложений уровня пользователя, созданных для систем HP-UX/PA-RISC, в том числе и тех, которые сейчас только разрабатываются. Однако, есть и ряд исключений. Например, он не может корректно эмулировать отладчик, созданный для систем HP-UX/PA-RISC из-за оптимизаций в оттранслированном коде. Кроме того, он не может пока эмулировать приложения, которые связаны с разделяемыми библиотеками и PA-RISC, и IA-64.

Мы рассматриваем динамическую трансляцию как важный метод переноса программного обеспечения, но последнее - это всего лишь одна из множества областей, где применение такого подхода способно обеспечить немалые преимущества. Эта технология, к примеру, способна помочь при сборе профильной информации в пакетах инструментальных средств для анализа производительности. Она может также стать базовой для реализации оптимизации времени исполнения в таких продуктах, как виртуальная машина Java. В то же время мы считаем, что она может оказать существенную помощь потребителям HP и позволит им перейти на более мощные системы IA-64 с минимальными затратами.

Об авторax

Кинди (Кингхуа) Зенг - старший инженер по созданию программного обеспечения отдела Adaptive Systems лаборатории Enterprise Java Lab корпорации Hewlett-Packard. С ней можно связаться по адресу cindy_zheng@hp.com

Кэрол Томпсон - специалист по разработке компиляторов для языков Си и C++ лаборатории Development Environment Solutions Lab корпорации Hewlett-Packard. К ее основным научным интересам относятся оптимизация и определение архитектур для IA-64 и PA-RISC. С ней можно связаться по адресу carol_thompson@hp.com


PA-RISC to IA-64: Transparent Execution, No Recompilation, Cindy Zheng, Carol Thompson. IEEE Computer, March 2000, pp. 47-52, Reprinted with permission, Copyright IEEE CS, 2000, All rights reserved.