stringtranslate.com

Статическая форма с одним заданием

В дизайне компилятора статическая форма одиночного присваивания (часто сокращенно SSA-форма или просто SSA ) — это тип промежуточного представления (IR) , где каждая переменная присваивается ровно один раз. SSA используется в большинстве высококачественных оптимизирующих компиляторов для императивных языков, включая LLVM , GNU Compiler Collection и многие коммерческие компиляторы.

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

SSA упрощает выполнение многочисленных анализов, необходимых для оптимизации, таких как определение цепочек use-define , поскольку при рассмотрении использования переменной есть только одно место, где эта переменная могла получить значение. Большинство оптимизаций можно адаптировать для сохранения формы SSA, так что одна оптимизация может выполняться за другой без дополнительного анализа. Оптимизации на основе SSA обычно более эффективны и мощны, чем их предыдущие эквиваленты без формы SSA.

В компиляторах функциональных языков , таких как для Scheme и ML , обычно используется стиль продолжения-передачи (CPS). SSA формально эквивалентен хорошо работающему подмножеству CPS, исключая нелокальный поток управления, поэтому оптимизации и преобразования, сформулированные в терминах одного, обычно применяются к другому. Использование CPS в качестве промежуточного представления более естественно для функций высшего порядка и межпроцедурного анализа. CPS также легко кодирует call/cc , тогда как SSA этого не делает. [1]

История

SSA была разработана в 1980-х годах несколькими исследователями из IBM . Кеннет Задек, ключевой член команды, перешел в Университет Брауна по мере продолжения разработки. [2] [3] В статье 1986 года были представлены точки рождения, назначения идентичности и переименование переменных таким образом, чтобы переменные имели единственное статическое назначение. [4] Последующая статья 1987 года Жанны Ферранте и Рональда Ситрона [5] доказала, что переименование, сделанное в предыдущей статье, устраняет все ложные зависимости для скаляров. [3] В 1988 году Барри Розен, Марк Н. Вегман и Кеннет Задек заменили назначения идентичности на Φ-функции, ввели название «статическая форма с одним назначением» и продемонстрировали ныне распространенную оптимизацию SSA. [6] Название Φ-функция было выбрано Розеном как более публикуемая версия «фальшивой функции». [3] Альперн, Вегман и Задек представили другую оптимизацию, но используя название «статическое одиночное присваивание». [7] Наконец, в 1989 году Розен, Вегман, Задек, Ситрон и Ферранте нашли эффективный способ преобразования программ в форму SSA. [8]

Преимущества

Основная польза SSA заключается в том, что он одновременно упрощает и улучшает результаты различных оптимизаций компилятора , упрощая свойства переменных. Например, рассмотрим этот фрагмент кода:

у := 1у := 2х := у

Люди могут видеть, что первое назначение не является необходимым, и что ценность yиспользования в третьей строке исходит из второго назначения y. Программа должна была бы выполнить анализ определения достижения, чтобы определить это. Но если программа находится в форме SSA, оба эти действия выполняются немедленно:

у 1  := 1у 2  := 2х 1  := у 2

Алгоритмы оптимизации компилятора , которые либо становятся доступными, либо значительно улучшаются благодаря использованию SSA, включают:

Конвертация в SSA

Преобразование обычного кода в форму SSA в первую очередь заключается в замене цели каждого назначения новой переменной и замене каждого использования переменной на «версию» переменной, достигшей этой точки. Например, рассмотрим следующий график потока управления :

Пример графа потока управления до преобразования в SSA

Изменение имени слева от "x x - 3" и изменение последующих использований x на это новое имя оставит программу без изменений. Это можно использовать в SSA, создав две новые переменные: x 1 и x 2 , каждая из которых назначается только один раз. Аналогично, присваивание отличительных индексов всем остальным переменным дает:

Пример графа потока управления, частично преобразованного в SSA

Понятно, к какому определению относится каждое использование, за исключением одного случая: оба использования y в нижнем блоке могут относиться либо к y 1 , либо к y 2 , в зависимости от того, по какому пути пойдет поток управления.

Чтобы решить эту проблему, в последний блок вставляется специальный оператор, называемый функцией Φ (Phi) . Этот оператор сгенерирует новое определение y, называемое y 3 , «выбирая» либо y 1 , либо y 2 , в зависимости от потока управления в прошлом.

Пример графа потока управления, полностью преобразованного в SSA

Теперь последний блок может просто использовать y 3 , и правильное значение будет получено в любом случае. Функция Φ для x не нужна: только одна версия x , а именно x 2 , достигает этого места, так что проблемы нет (другими словами, Φ( x 2 , x 2 )= x 2 ).

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

Функции Φ не реализованы как машинные операции на большинстве машин. Компилятор может реализовать функцию Φ, вставив операции «перемещения» в конец каждого предшествующего блока. В приведенном выше примере компилятор может вставить перемещение из y 1 в y 3 в конец среднего левого блока и перемещение из y 2 в y 3 в конец среднего правого блока. Эти операции перемещения могут не попасть в конечный код на основе процедуры распределения регистров компилятора . Однако этот подход может не работать, когда одновременные операции спекулятивно производят входные данные для функции Φ, как это может произойти на машинах с широким выпуском . Обычно машина с широким выпуском имеет инструкцию выбора, используемую в таких ситуациях компилятором для реализации функции Φ.

Вычисление минимального SSA с использованием границ доминирования

В графе потока управления узел A считается строго доминирующим над другим узлом B, если невозможно достичь B, не пройдя сначала через A. Другими словами, если достигнут узел B, то можно предположить, что A выполнилось. Говорят, что A доминирует над B (или B доминируется A), если либо A строго доминирует над B, либо A = B.

Узел, который передает управление узлу A, называется непосредственным предшественником A.

Граница доминирования узла A — это набор узлов B, где A не доминирует строго над B, но доминирует над некоторым непосредственным предшественником B. Это точки, в которых несколько путей управления снова сливаются в один путь.

Например, в следующем коде:

[1] x = случайный()если х < 0,5 [2] результат = "орел"еще [3] результат = "решка"конец[4] печать(результат)

Узел 1 строго доминирует над 2, 3 и 4, а непосредственными предшественниками узла 4 являются узлы 2 и 3.

Границы доминирования определяют точки, в которых необходимы функции Φ. В приведенном выше примере, когда управление передается узлу 4, определение used resultзависит от того, было ли управление передано из узла 2 или 3. Функции Φ не нужны для переменных, определенных в доминаторе, поскольку существует только одно возможное определение, которое может применяться.

Существует эффективный алгоритм для поиска границ доминирования каждого узла. Этот алгоритм был первоначально описан в "Efficiently Computing Static Single Assignment Form and the Control Graph" Рона Ситрона, Жанны Ферранте и др. в 1991 году. [10]

Кит Д. Купер, Тимоти Дж. Харви и Кен Кеннеди из Университета Райса описывают алгоритм в своей статье под названием «Простой, быстрый алгоритм доминирования» : [11]

для каждого узла b граница_доминирования(б) := {}для каждого узла b , если число непосредственных предшественников b ≥ 2 для каждого p в непосредственных предшественниках b бегун := p пока бегун ≠ idom(b) доминирование_граница(бегун) := доминирование_граница(бегун) ∪ { b } бегун := idom(бегун)

В приведенном выше коде idom(b)непосредственный доминант b, уникальный узел, который строго доминирует над b, но не доминирует строго над любым другим узлом, который строго доминирует над b.

Вариации, уменьшающие количество функций Φ

«Минимальный» SSA вставляет минимальное количество функций Φ, необходимое для того, чтобы гарантировать, что каждому имени присвоено значение ровно один раз и что каждая ссылка (использование) имени в исходной программе может по-прежнему ссылаться на уникальное имя. (Последнее требование необходимо для того, чтобы гарантировать, что компилятор может записать имя для каждого операнда в каждой операции.)

Однако некоторые из этих функций Φ могут быть мертвыми . По этой причине минимальный SSA не обязательно производит наименьшее количество функций Φ, необходимых для определенной процедуры. Для некоторых типов анализа эти функции Φ являются излишними и могут привести к тому, что анализ будет выполняться менее эффективно.

Сокращенный SSA

Сокращенная форма SSA основана на простом наблюдении: функции Φ нужны только для переменных, которые являются «живыми» после функции Φ. (Здесь «живыми» означает, что значение используется на некотором пути, который начинается с рассматриваемой функции Φ.) Если переменная не является живой, результат функции Φ не может быть использован, и присвоение функцией Φ недействительно.

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

Другая возможность — рассматривать обрезку как проблему устранения мертвого кода . Тогда функция Φ является живой, только если любое использование во входной программе будет перезаписано в нее, или если она будет использована в качестве аргумента в другой функции Φ. При вводе формы SSA каждое использование перезаписывается в ближайшее определение, которое доминирует над ней. Функция Φ будет затем считаться живой, пока это ближайшее определение, которое доминирует хотя бы над одним использованием или хотя бы над одним аргументом живой Φ.

Полуобрезанный SSA

Полуобрезанная форма SSA [12] — это попытка сократить количество функций Φ без относительно высокой стоимости вычисления информации о живых переменных. Она основана на следующем наблюдении: если переменная никогда не является живой при входе в базовый блок, ей никогда не понадобится функция Φ. Во время построения SSA функции Φ для любых «локальных для блока» переменных опускаются.

Вычисление набора локальных переменных блока является более простой и быстрой процедурой, чем полный анализ живых переменных, что делает полуобрезанную форму SSA более эффективной для вычисления, чем обрезанную форму SSA. С другой стороны, полуобрезанная форма SSA будет содержать больше функций Φ.

Блокировать аргументы

Аргументы блока являются альтернативой функциям Φ, которая по представлению идентична, но на практике может быть более удобной во время оптимизации. Блоки именуются и принимают список аргументов блока, обозначенных как параметры функции. При вызове блока аргументы блока привязываются к указанным значениям. MLton , Swift SIL и LLVM MLIR используют аргументы блока. [13]

Преобразование из формы SSA

Форма SSA обычно не используется для прямого выполнения (хотя можно интерпретировать SSA [14] ), и она часто используется «поверх» другого IR, с которым она остается в прямом соответствии. Это может быть достигнуто путем «построения» SSA как набора функций, которые отображают части существующего IR (базовые блоки, инструкции, операнды и т. д. ) и его аналог SSA. Когда форма SSA больше не нужна, эти функции отображения могут быть отброшены, оставив только оптимизированный IR.

Выполнение оптимизаций в форме SSA обычно приводит к запутанным SSA-сетям, то есть существуют инструкции Φ, операнды которых не все имеют одинаковый корневой операнд. В таких случаях для выхода из SSA используются алгоритмы раскрашивания . Наивные алгоритмы вводят копию вдоль каждого предшествующего пути, что приводит к тому, что источник корневого символа, отличного от назначения Φ, помещается в Φ. Существует несколько алгоритмов для выхода из SSA с меньшим количеством копий, большинство из них используют интерференционные графы или некоторую их аппроксимацию для объединения копий. [15]

Расширения

Расширения формы SSA можно разделить на две категории.

Расширения схемы переименования изменяют критерий переименования. Напомним, что форма SSA переименовывает каждую переменную, когда ей присваивается значение. Альтернативные схемы включают статическую форму одноразового использования (которая переименовывает каждую переменную в каждом операторе, когда она используется) и статическую форму одноразовой информации (которая переименовывает каждую переменную, когда ей присваивается значение, и на границе постдоминирования).

Расширения , специфичные для функций , сохраняют свойство единственного назначения для переменных, но включают новую семантику для моделирования дополнительных функций. Некоторые расширения, специфичные для функций, моделируют высокоуровневые функции языка программирования, такие как массивы, объекты и псевдонимные указатели. Другие расширения, специфичные для функций, моделируют низкоуровневые архитектурные функции, такие как спекуляция и предикация.

Компиляторы, использующие форму SSA

С открытым исходным кодом

Коммерческий

Исследования и заброшенные

Ссылки

Примечания

  1. ^ Келси, Ричард А. (1995). "Соответствие между стилем передачи продолжения и формой статического одиночного присваивания" (PDF) . Статьи с семинара ACM SIGPLAN 1995 года по промежуточным представлениям . стр. 13–22. doi :10.1145/202529.202532. ISBN 0897917545. S2CID  6207179.
  2. ^ Растелло и Тичаду 2022, разд. 1.4.
  3. ^ abc Задек, Кеннет (апрель 2009 г.). Разработка статической формы одиночного задания (PDF) . Семинар по статической форме одиночного задания. Отранс, Франция.
  4. ^ Cytron, Ron; Lowry, Andy; Zadeck, F. Kenneth (1986). «Кодовое движение управляющих структур в языках высокого уровня». Труды 13-го симпозиума ACM SIGACT-SIGPLAN по принципам языков программирования — POPL '86 : 70–85. doi :10.1145/512644.512651. S2CID  9099471.
  5. ^ Cytron, Ronald Kaplan; Ferrante, Jeanne. Что в имени? Или значение переименования для обнаружения параллелизма и распределения памяти . Международная конференция по параллельной обработке, ICPP'87 1987. стр. 19–27.
  6. ^ Барри Розен; Марк Н. Вегман; Ф. Кеннет Задек (1988). "Глобальные числа значений и избыточные вычисления" (PDF) . Труды 15-го симпозиума ACM SIGPLAN-SIGACT по принципам языков программирования .
  7. ^ Альперн, Б.; Вегман, МН; Задек, ФК (1988). «Обнаружение равенства переменных в программах». Труды 15-го симпозиума ACM SIGPLAN-SIGACT по принципам языков программирования - POPL '88 . стр. 1–11. doi :10.1145/73560.73561. ISBN 0897912527. S2CID  18384941.
  8. ^ Cytron, Ron; Ferrante, Jeanne; Rosen, Barry K.; Wegman, Mark N. & Zadeck, F. Kenneth (1991). "Эффективное вычисление статической одиночной формы присваивания и графа зависимости управления" (PDF) . ACM Transactions on Programming Languages ​​and Systems . 13 (4): 451–490. CiteSeerX 10.1.1.100.6361 . doi :10.1145/115372.115320. S2CID  13243943. 
  9. ^ распространение диапазона значений
  10. ^ Cytron, Ron; Ferrante, Jeanne; Rosen, Barry K.; Wegman, Mark N.; Zadeck, F. Kenneth (1 октября 1991 г.). «Эффективное вычисление статической формы одиночного присваивания и графа зависимости управления». ACM Transactions on Programming Languages ​​and Systems . 13 (4): 451–490. doi : 10.1145/115372.115320 . S2CID  13243943.
  11. ^ Купер, Кит Д.; Харви, Тимоти Дж.; Кеннеди, Кен (2001). Простой, быстрый алгоритм доминирования (PDF) (технический отчет). Университет Райса, CS технический отчет 06-33870. Архивировано из оригинала (PDF) 2022-03-26.
  12. ^ Бриггс, Престон; Купер, Кит Д.; Харви, Тимоти Дж.; Симпсон, Л. Тейлор (1998). Практические улучшения в построении и разрушении статической формы одиночного назначения (PDF) (технический отчет). Архивировано из оригинала (PDF) 2010-06-07.
  13. ^ "Аргументы блока против узлов PHI - Обоснование MLIR". mlir.llvm.org . Получено 4 марта 2022 г. .
  14. ^ фон Ронне, Джеффри; Нин Ванг; Майкл Франц (2004). "Интерпретация программ в статической форме одиночного присваивания". Труды семинара 2004 года по интерпретаторам, виртуальным машинам и эмуляторам - IVME '04. стр. 23. doi :10.1145/1059579.1059585. ISBN 1581139098. S2CID  451410.
  15. ^ Буассино, Бенуа; Дарте, Ален; Растелло, Фабрис; Динешен, Бенуа Дюпон де; Гийон, Кристоф (2008). «Пересмотр перевода за пределами SSA для обеспечения корректности, качества кода и эффективности». ХАЛ-Инрия Cs.DS : 14.
  16. ^ "Представляем WebKit FTL JIT". 13 мая 2014 г.
  17. ^ «Представляем компилятор B3 JIT». 15 февраля 2016 г.
  18. ^ "Swift Intermediate Language (GitHub)". GitHub . 30 октября 2021 г.
  19. ^ "Высокоуровневый IR Swift: пример дополнения LLVM IR с языковой оптимизацией, встреча разработчиков LLVM 10/2015". YouTube . 9 ноября 2015 г. Архивировано из оригинала 21.12.2021.
  20. ^ «Примечания к выпуску OTP 22.0».
  21. ^ "Go 1.7 Release Notes - The Go Programming Language". golang.org . Получено 2016-08-17 .
  22. ^ "Go 1.8 Release Notes - The Go Programming Language". golang.org . Получено 2017-02-17 .
  23. ^ "Обзор IonMonkey".,
  24. ^ Эволюция ИСКУССТВА - Google I/O 2016. Google . 25 мая 2016 г. Событие происходит в 3:47.
  25. ^ «Оптимизация байт-кода». Проект LuaJIT.
  26. ^ "HipHop Intermediate Representation (HHIR)". GitHub . 30 октября 2021 г.
  27. ^ «Фирма — Оптимизация и генерация машинного кода».
  28. Экстранд, Джейсон (16 декабря 2014 г.). «Вновь представляем NIR, новый IR для Месы».
  29. ^ «Архитектура Java HotSpot Performance Engine». Корпорация Oracle.
  30. ^ «Представляем новый, усовершенствованный оптимизатор кода Visual C++». 4 мая 2016 г.
  31. ^ "SPIR-V spec" (PDF) .
  32. ^ Саркар, В. (май 1997 г.). «Автоматический выбор преобразований высокого порядка в компиляторах IBM XL FORTRAN» (PDF) . IBM Journal of Research and Development . 41 (3). IBM: 233–264. doi :10.1147/rd.413.0233.
  33. ^ Чакрабарти, Гаутам; Гровер, Винод; Аартс, Бастиан; Конг, Сянъюнь; Кудлур, Манджунат; Линь, Юань; Марат, Джейдип; Мерфи, Майк; Ван, Цзянь-Чжун (2012). «CUDA: Компиляция и оптимизация для платформы GPU». Procedia Computer Science . 9 : 1910–1919. doi : 10.1016/j.procs.2012.04.209 .
  34. ^ "Illinois Concert Project". Архивировано из оригинала 2014-03-13.
  35. ^ Ананян, К. Скотт; Ринард, Мартин (1999). Статическая единая информационная форма (PDF) (Технический отчет). CiteSeerX 10.1.1.1.9976 . 
  36. ^ Энциклопедия параллельных вычислений.

Общие ссылки

Внешние ссылки