Филипп Хислей - Генерация высококачественного кода для программ, написанных на СИ
Manx Software Systems Inc.
Компилятор Aztec C86 сгенерировал хороший код с довольно хорошим уровнем оптимизации. Кроме свертки констант и алгебраических упрощений, Aztec C86 выполнил снижение мощности и удаление общих подвыражений. Однако, он не выполнил удаление лишних присваиваний и не удалял недостижимый код. Aztec C86 сгенерировал код для недостижимого оператора printf вместе с безусловным переходом через него.
Поскольку любая программа на Си имеет значительное количество вызовов функций, заголовок каждого вызова необходимо минимизировать. Aztec C86 использует необычный, но эффективный подход к решению этой проблемы. На выходе компилятора получается текст в языке ассемблера, который обрабатывается отдельным ассемблером. Компилятор вставляет в текст директивы условного ассемблирования вокруг кода, который устанавливает стековый фрейм и сохраняет регистры. После генерации кода функции компилятор определяет символы для управления установкой стекового фрейма и сохранения только тех регистров, которые используются в функции.
Aztec C86 не смог решить задачу преобразования цепочки переходов в один переход к конечной цели. Он также не выполнял оптимизацию циклов.
Metaware Inc.
High C вырабатывает хороший код со средним уровнем оптимизации. Компилятор выполняет все базовые виды оптимизации, включая свертку констант и алгебраические упрощения, удаление лишних операций загрузки регистров, снижение мощности и удаление общих подвыражений. Компилятор Metaware удаляет недостижимый код из функции dead_code, но не удаляет лишние присваивания.
High C разумно использует машинно-зависимые инструкции. Компилятор усовершенствует загрузку констант с плавающей точкой, используя команду копирования строк MOVS процессоров 80x86 для записи значений с плавающей точкой, вычисленных во время компиляции. Он также генерирует инструкцию LEAVE процессоров 80x86 для эпилога функций, но устанавливает адресацию стекового фрейма в прологе функции с помощью отдельных инструкций, а не используя более длительную инструкцию ENTER.
Компилятор High C не выполняет вынесение инвариантного кода, важный метод оптимизации циклов. Он также не смог применить успешно удаление переменных индукции циклов. Встроенные функции поддерживаются для нескольких целочисленных и строковых операций, таких как strlen.
Microsoft C.
В версии 5.0 своего компилятора Си корпорация Microsoft вывела высокий уровень оптимизации кода на рынок PC. Microsoft уделяет много внимания анализу циклов. C 5.0 – единственный из рассматриваемых компиляторов, который выполняет вынесение инвариантного кода и настоящее удаление переменных индукции циклов. Компилятор Microsoft C 5.0 превосходно использует регистры, стараясь минимизировать обращения к памяти в теле цикла (см. рис. 4 и 5).
Простой пример цикла в коде теста демонстрирует степень оптимизации циклов, выполняемой Microsoft C 5.0 (см. рис. 3). Компилятор применяет снижение мощности и полностью удаляет константное умножение, выявляет конечное состояние переменных j5 и k5, и помещает в регистры все переменные внутри цикла.
Другой хороший пример оптимизации циклов этим компилятором отражен в функции unnecessary_loop. C 5.0 удаляет цикл for и генерирует код только с целью установки конечного состояния переменной - индекса цикла и оператора, включенного в цикл. Компилятор также хорошо использует регистры.
Внимание фирмы Microsoft к оптимизации вознаграждается при работе теста выполнения. Он выполняется за время, которое является лучшим или близко к лучшему по каждой категории.
Microsoft QuickC.
Когда речь идет об оптимизации, QuickC становится настолько беспомощным, насколько C 5.0 изощренным. Код, сгенерированный QuickC, был в основном дословным переводом, насыщенным излишними загрузками и сохранениями регистров, переходами на переходы. Этот компилятор применяет лишь наиболее первичные методы оптимизации, свертку констант и некоторые алгебраические упрощения. Он сгенерировал недостижимый код, поместил переход через него и не смог выполнить сжатие цепочки переходов.
В пользу компилятора свидетельствует то, что что он разумно управляет прологами и эпилогами функций, используя отдельные инструкции для установки адресации стекового фрейма при входе и инструкцию LEAVE при завершении функции. При входе сохраняются и при выходе восстанавливаются только те регистры, которые используются в теле функции.
QuickC был влючен в этот обзор, потому что он имеет ключ оптимизации в командной строке (-Ox). Генерируя код, который по своей природе - дословный перевод исходного текста, QuickC был разработан исключительно как быстрый прототип компилятора, но не как оптимизирующий компилятор.
WATCOM.
Новейший соперник, завоевывающий позиции на рынке компиляторов C - WATCOM C 6.0 (см. Product Watch, Philip N. Hisley, за этот месяц). C 6.0 вырабатывает компактный код, который прекрасно использует несколько ограниченный комплект регистров семейства 80x86. Кроме выполнения базовых приемов оптимизации, он поддерживает снижение мощности и удаление недостижимого кода и общих подвыражений. В то время, как Microsoft достигает улучшения кода благодаря оптимизации циклов, WATCOM увеличивает скорость путем уменьшения управляющих заголовков вызовов функций к их абсолютно минимальному размеру. Он достигает этого путем преимущественной передачи параметров через регистры, а не через стек.
WATCOM очень хорошо удаляет недостижимый код. C 6.0 не только удалил ненужные присваивания и недостижимый код внутри функции, но он также удалил пролог и эпилог функции и свернул всю функцию к простому возврату, приписав имя функции к инструкции возврата основной функции. В завершение всего, компилятор удалил локальный вызов функции.
Насколько C 6.0 изощрен в уничтожении бесполезной функции, настолько же он беспомощен при удалении бесполезного дублирующегося присваивания. Наиболее важная область, за которую WATCOM C 6.0, как и Optimum-C, не смог взяться, была оптимизация циклов. Он не поддерживает вынесение инвариантного кода и удаление переменных индукции циклов.
Хотя C 6.0 не выполняет разворачивание циклов в отдельные команды, он (также как Datalight Optimum-C и Computer Innovations C86Plus) использует команду REP/STOSW процессоров 80x86 для инициализации тестового массива, благодаря чему удаляет цикл.
Прекрасная генерация кода в WATCOM, в частности, разумное использование регистров, дает ему очень важное преимущество. В тесте выполнения он победил в большинстве тестов, интенсивно использующих процессор, и при этом выполнялся для большой модели в лучшее время, чем большинство других компиляторов для малой модели. К слабым сторонам WATCOM можно отнести ввод/вывод файлов, использование getc и putc. Здесь он близок к наихудшим компиляторам.
Выявленные лидеры
По существующему определению, любой компилятор, который выполняет не буквальное отображение исходного текста, выполняет некоторый вид оптимизации, даже если преобразование это такое низкоуровневое, как свертка констант. Минимальный приемлемый уровень оптимизации будет возрастать по мере того, как доступная на рынке технология генерации кода будет предоставлять более глубокие методы оптимизации. На сегодняшнем уровне технологии минимальным приемлемым уровнем оптимизации представляется удаление общих подвыражений. Этот уровень подразумевает, что компиляторы, которые выполняют удаление общих подвыражений, также выполняют основные приемы оптимизации, такие как свертка констант и алгебраические упрощения.
Даже с установленным минимальным уровнем оценка возможностей конкретных компиляторов усложняется существованием многих несоизмеримых форм оптимизации. Компилятор может хорошо использовать регистры, но не поддерживать удаление общих подвыражений. Поскольку оптимизированный код зависит не только от применяемых методов, но также и от структуры программы, которая оптимизировалась, в общем случае было бы заблуждением считать, что один компилятор лучше другого, опираясь исключительно на один отдельный тест.
Хотя все девять рассматриваемых компиляторов генерируют приемлемый код, три из них, - Datalight Optimum-C, Microsoft C 5.0 и WATCOM C 6.0, - выполняют оптимизацию кода более высокого уровня, чем остальные.
Компилятор Datalight Optimum-C - это быстрый и выразительный исполнитель. Он выполняет обширный анализ потоков данных и оптимизирует код, за который другие компиляторы не берутся.
Microsoft C 5.0 применяет оптимизацию циклов, которая является одной из областей с большими потенциальными возможностями улучшения кода. Применяя вынесение инвариантного кода, удаление переменных индукции циклов и очень качественное распределение переменных по регистрам, Microsoft C 5.0 вырабатывает прекрасный код.
Компилятор WATCOM C 6.0 соперничает с Microsoft C 5.0 по степени выполняемой оптимизации и генерирует наиболее быстрый код в тесте оптимизации. То, что WATCOM теряет на не самых оптимальных циклах, он более чем наверстывает в малых заголовках вызова функций. WATCOM C 6.0 хорошо использует регистры, минимизирует обращения к памяти и повышает эффективность выполнения программ.