МЛИР (программное обеспечение)
Разработчик(и) | Группа разработчиков LLVM |
---|---|
Написано в | С++ |
Операционная система | Кросс-платформенный |
Тип | Компилятор |
Веб-сайт | млир |
MLIR — это объединяющая программная среда для разработки компиляторов . [1] MLIR может оптимально использовать различные вычислительные платформы , такие как графические процессоры , DPU , TPU , FPGA , AI ASICS и системы квантовых вычислений (QPU). [2]
MLIR является подпроектом проекта LLVM Compiler Infrastructure и направлен на создание «многоразовой и расширяемой инфраструктуры компилятора (..) и помощи в объединении существующих компиляторов». [3] [4] [5]
Имя
[ редактировать ]Название проекта расшифровывается как Multi-Level Intermediate Representation , в котором ключевое слово multi-level относится к возможности определения нескольких диалектов и прогрессивных преобразований в сторону машинного кода . Эта возможность позволяет MLIR сохранять информацию на более высоком уровне абстракции и выполнять более точный анализ и преобразования, которым в противном случае пришлось бы иметь дело с представлениями более низкого уровня. [6]
Диалекты
[ редактировать ]Операции представляют собой основной элемент, вокруг которого строятся диалекты. Они идентифицируются по имени, которое должно быть уникальным в пределах диалекта, к которому они принадлежат, и имеют дополнительные операнды , результаты, атрибуты и регионы . Операнды и результаты соответствуют форме статического одиночного присваивания . Каждый результат также имеет связанный тип. Атрибуты представляют собой знания времени компиляции (например, постоянные значения). Регионы состоят из списка блоков, каждый из которых может иметь входные аргументы и содержать список операций. [7] Несмотря на то, что диалекты созданы на основе формы SSA, узлы PHI не являются частью этой конструкции и вместо этого заменяются входными аргументами блоков в сочетании с операндами операций потока управления . [8]
Общий синтаксис операции следующий:
%res:2 = "mydialect.morph"(%input#3) ({ ^bb0(%arg0: !mydialect<"custom_type"> loc("mysource.cc":10:8)): // nested operations }) { some.attribute = true, other_attribute = 1.5 } : (!mydialect<"custom_type">) -> (!mydialect<"other_type">, !mydialect<"other_type">) loc(callsite("foo" at "mysource.cc":10:8))
В примере показана операция с именем morph , принадлежащая диалекту mydialect , принимающая один входной операнд и выдающая два результата. Входной аргумент имеет связанный тип с именем custom_type , а оба результата имеют типother_type , причем оба типа снова принадлежат диалекту mydialect . У операции также есть два связанных атрибута — с именами some.attribute иother_attribute — и область, содержащая один блок. Наконец, с помощью ключевого слова loc добавляются местоположения для целей отладки . [9]
Синтаксис операций, типов и атрибутов также можно настроить в соответствии с предпочтениями пользователя путем реализации соответствующих синтаксического анализа и печати функций в определении операции. [10]
Основные диалекты
[ редактировать ]Экосистема диалектов MLIR открыта и расширяема, а это означает, что конечные пользователи могут свободно создавать новые диалекты, отражающие необходимую им семантику. Тем не менее, кодовая база MLIR уже предоставляет конечным пользователям доступ к различным типам диалектов. Каждый из них направлен на рассмотрение определенного аспекта, который часто проявляется в промежуточных представлениях , но делает это автономным образом. Например, диалект арифита поддерживает простые математические операции над целочисленными значениями и значениями с плавающей запятой , а диалект memref содержит операции по управлению памятью. [11]
Следующий код определяет функцию, которая принимает две матрицы с плавающей запятой и выполняет суммирование значений в одних и тех же позициях:
func.func @matrix_add(%arg0: memref<10x20xf32>, %arg1: memref<10x20xf32>) -> memref<10x20xf32> { %result = memref.alloc() : memref<10x20xf32> affine.for %i = 0 to 10 { affine.for %j = 0 to 20 { %lhs = memref.load %arg0[%i, %j] : memref<10x20xf32> %rhs = memref.load %arg1[%i, %j] : memref<10x20xf32> %sum = arith.addf %lhs, %rhs : f32 memref.store %sum, %result[%i, %j] : memref<10x20xf32> } } func.return %result : memref<10x20xf32>}
Для достижения одного и того же результата могут использоваться разные диалекты, и каждый из них может подразумевать разные уровни абстракции. В этом примере аффинный диалект был выбран для повторного использования существующих анализов и оптимизаций для многогранной компиляции . [11]
Одним из соответствующих основных диалектов является LLVM. Его цель — предоставить однозначное отображение LLVM-IR — промежуточного представления, используемого LLVM, — чтобы обеспечить возможность повторного использования всех его промежуточных и внутренних преобразований, включая генерацию машинного кода. [12]
Спецификация определения операции
[ редактировать ]Операции диалекта можно определить с помощью языка C++ , а также более удобным и надежным способом с помощью спецификации определения операции (ODS). [13] Используя TableGen, можно автоматически генерировать код C++ для объявлений и определений. [14]
Автоматически сгенерированный код может включать методы синтаксического анализа и печати, которые основаны на простом сопоставлении строк со структурой желаемого текстового представления, вместе со всем шаблонным кодом для доступа к полям и выполнения общих действий, таких как проверка семантики каждой операции, канонизация или свертывание. . [15]
Тот же механизм объявления можно использовать и для типов и атрибутов, которые являются двумя другими категориями элементов, составляющих диалект. [15]
В следующем примере показано, как указать ассемблерный формат операции, ожидающей вариативное количество операндов и дающей нулевые результаты. Текстовое представление состоит из необязательного списка атрибутов, за которым следует необязательный список операндов, двоеточие и типы операндов. [13]
let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?";
Преобразования
[ редактировать ]Преобразования всегда можно выполнять непосредственно на IR, не полагаясь на встроенные механизмы координации. Однако, чтобы упростить внедрение и обслуживание, MLIR предоставляет инфраструктуру для перезаписи IR, состоящую из различных драйверов перезаписи. Каждый драйвер получает набор объектов , называемых шаблонами , каждый из которых имеет собственную внутреннюю логику для сопоставления операций с определенными свойствами. При совпадении операции выполняется процесс перезаписи, и IR изменяется в соответствии с логикой шаблона. [16]
Драйвер преобразования диалектов
[ редактировать ]Этот драйвер действует в соответствии с законностью существующих операций. Это означает, что драйвер получает набор правил, определяющих, какие операции следует считать незаконными , и ожидает, что шаблоны совпадут и преобразуют их в законные . Логика этих правил может быть сколь угодно сложной: она может основываться только на диалекте, к которому принадлежат операции, но также может проверять более конкретные свойства, такие как атрибуты или вложенные операции. [17]
Как следует из названия, этот драйвер обычно используется для преобразования операций одного диалекта в операции, принадлежащие другому диалекту. В этом сценарии весь исходный диалект будет помечен как незаконный, а целевой — как легальный, и будут предоставлены шаблоны для операций с исходным диалектом. Платформа преобразования диалектов также обеспечивает поддержку преобразования типов, которое необходимо выполнить для операндов и результатов, чтобы преобразовать их в систему типов целевого диалекта. [17]
MLIR позволяет использовать несколько путей преобразования. Учитывая пример суммы матриц, возможной стратегией понижения может быть создание циклов for, принадлежащих диалекту scf , с получением кода для выполнения на процессорах :
#map = affine_map<(d0, d1) -> (d0, d1)>module { func.func @avg(%arg0: memref<10x20xf32>, %arg1: memref<10x20xf32>) -> memref<10x20xf32> { %alloc = memref.alloc() : memref<10x20xf32> %c0 = arith.constant 0 : index %c10 = arith.constant 10 : index %c1 = arith.constant 1 : index scf.for %arg2 = %c0 to %c10 step %c1 { %c0_0 = arith.constant 0 : index %c20 = arith.constant 20 : index %c1_1 = arith.constant 1 : index scf.for %arg3 = %c0_0 to %c20 step %c1_1 { %0 = memref.load %arg0[%arg2, %arg3] : memref<10x20xf32> %1 = memref.load %arg1[%arg2, %arg3] : memref<10x20xf32> %2 = arith.addf %0, %1 : f32 memref.store %2, %alloc[%arg2, %arg3] : memref<10x20xf32> } } return %alloc : memref<10x20xf32> }}
Однако другой возможной стратегией могло бы быть использование диалекта графического процессора для генерации кода для графических процессоров :
#map = affine_map<(d0, d1) -> (d0, d1)>module { func.func @avg(%arg0: memref<10x20xf32>, %arg1: memref<10x20xf32>) -> memref<10x20xf32> { %alloc = memref.alloc() : memref<10x20xf32> %c0 = arith.constant 0 : index %c10 = arith.constant 10 : index %0 = arith.subi %c10, %c0 : index %c1 = arith.constant 1 : index %c0_0 = arith.constant 0 : index %c20 = arith.constant 20 : index %1 = arith.subi %c20, %c0_0 : index %c1_1 = arith.constant 1 : index %c1_2 = arith.constant 1 : index gpu.launch blocks(%arg2, %arg3, %arg4) in (%arg8 = %0, %arg9 = %c1_2, %arg10 = %c1_2) threads(%arg5, %arg6, %arg7) in (%arg11 = %1, %arg12 = %c1_2, %arg13 = %c1_2) { %2 = arith.addi %c0, %arg2 : index %3 = arith.addi %c0_0, %arg5 : index %4 = memref.load %arg0[%2, %3] : memref<10x20xf32> %5 = memref.load %arg1[%2, %3] : memref<10x20xf32> %6 = arith.addf %4, %5 : f32 memref.store %4, %alloc[%2, %3] : memref<10x20xf32> gpu.terminator } return %alloc : memref<10x20xf32> }}
Драйвер перезаписи жадного шаблона
[ редактировать ]Драйвер жадно применяет предоставленные шаблоны в соответствии с их выгодой, пока не будет достигнута фиксированная точка или не будет достигнуто максимальное количество итераций. Преимущество шаблона присваивается самому себе. В случае равенства используется относительный порядок в списке шаблонов. [16]
Особенности и интерфейсы
[ редактировать ]MLIR позволяет применять существующие оптимизации (например, устранение общих подвыражений , перемещение кода, инвариантного к циклу ) к пользовательским диалектам с помощью признаков и интерфейсов. Эти два механизма позволяют проходам преобразования работать с операциями, не зная их фактической реализации, полагаясь только на некоторые свойства, предоставляемые типажами или интерфейсами. [18] [19]
Трейты предназначены для прикрепления к операциям без необходимости какой-либо дополнительной реализации. Их цель — указать, что операция удовлетворяет определенным свойствам (например, имеет ровно два операнда). [18] Вместо этого интерфейсы представляют собой более мощный инструмент, с помощью которого можно запросить операцию о каком-то конкретном аспекте, значение которого может меняться между экземплярами операции одного и того же типа. Примером интерфейса является представление эффектов памяти: к каждой операции, которая работает с памятью, может быть прикреплен такой интерфейс, но фактические эффекты могут зависеть от реальных операндов (например, вызов функции с аргументами, которые могут быть константами или ссылками на память). [19]
Приложения
[ редактировать ]Свобода моделирования промежуточных представлений позволяет использовать MLIR в широком диапазоне сценариев. Сюда входят традиционные языки программирования, [20] но и синтез высокого уровня , [21] [22] квантовые вычисления [23] и гомоморфное шифрование . [24] [25] [26] Приложения машинного обучения также используют преимущества встроенных методов многогранной компиляции вместе с диалектами, ориентированными на ускорители и другие гетерогенные системы . [27] [28] [29] [30] [31]
См. также
[ редактировать ]Ссылки
[ редактировать ]- ^ Ахо, Альфред В.; Сетхи, Рави; Уллман, Джеффри Д. (2002). Составители: принципы, методы и инструменты . Серия Аддисона-Уэсли по информатике (перепечатано с корр., [36. Драк] под ред.). Ридинг, Массачусетс: Аддисон-Уэсли. ISBN 978-0-201-10088-4 .
- ^ «Почему Моджо» . docs.modular.com . Модульная Инк. 2023 . Проверено 28 августа 2023 г.
Сильной стороной MLIR является его способность создавать компиляторы для конкретной предметной области, особенно для странных областей, которые не являются традиционными процессорами и графическими процессорами, таких как AI ASICS, системы квантовых вычислений, FPGA и специальные микросхемы.
- ^ «Инфраструктура компилятора LLVM» . ЛЛВМ . Проверено 1 октября 2023 г.
Подпроект MLIR — это новый подход к созданию повторно используемой и расширяемой инфраструктуры компилятора. MLIR направлен на решение проблемы фрагментации программного обеспечения, улучшение компиляции для гетерогенного оборудования, значительное снижение стоимости создания компиляторов для конкретной предметной области и помощь в объединении существующих компиляторов.
- ^ Латтнер, Крис; Амини, Мехди; Бондугула, Удай; Коэн, Альберт; Дэвис, Энди; Пиенаар, Жак; Риддл, Река; Шпейсман, Татьяна; Василаче, Николя; Зиненко, Александр (2021). «MLIR: инфраструктура масштабируемого компилятора для вычислений в конкретной области». Международный симпозиум IEEE/ACM по генерации и оптимизации кода (CGO) 2021 года . стр. 2–14. дои : 10.1109/CGO51591.2021.9370308 . ISBN 978-1-7281-8613-9 .
- ^ Мерник, Марьян; Хиринг, Ян; Слоан, Энтони М. (декабрь 2005 г.). «Когда и как разрабатывать предметно-ориентированные языки» . Обзоры вычислительной техники ACM . 37 (4): 316–344. дои : 10.1145/1118890.1118892 . ISSN 0360-0300 . S2CID 207158373 .
- ^ Зайдль, Гельмут; Вильгельм, Рейнхард; Хак, Себастьян (2012). Проектирование компилятора: анализ и преобразование . Берлин Нью-Йорк: Спрингер. ISBN 978-3-642-17548-0 .
- ^ «Справочник по языку MLIR — MLIR» . mlir.llvm.org . Проверено 5 июля 2023 г.
- ^ «Обоснование MLIR – MLIR» . mlir.llvm.org . Проверено 5 июля 2023 г.
- ^ Мехди, Амини; Река, Риддл. «Учебное пособие по MLIR» (PDF) .
- ^ Страуструп, Бьярне (2015). Язык программирования C++: C++ 11 (4-е изд., 4-е печатное изд.). Река Аппер-Сэддл, Нью-Джерси: Аддисон-Уэсли. ISBN 978-0-321-56384-2 .
- ^ Перейти обратно: а б «Диалекты – МЛИР» . mlir.llvm.org . Проверено 7 июля 2023 г.
- ^ «Справочное руководство по языку LLVM — документация LLVM 17.0.0git» . llvm.org . Проверено 5 июля 2023 г.
- ^ Перейти обратно: а б «Спецификация определения операции (ODS) — MLIR» . mlir.llvm.org . Проверено 5 июля 2023 г.
- ^ «Обзор TableGen — документация LLVM 17.0.0git» . llvm.org . Проверено 5 июля 2023 г.
- ^ Перейти обратно: а б «Определение диалектов – MLIR» . mlir.llvm.org . Проверено 7 июля 2023 г.
- ^ Перейти обратно: а б «Переписывание шаблонов: общее переписывание DAG-to-DAG — MLIR» . mlir.llvm.org . Проверено 6 июля 2023 г.
- ^ Перейти обратно: а б «Преобразование диалектов – МЛИР» . mlir.llvm.org . Проверено 6 июля 2023 г.
- ^ Перейти обратно: а б «Черты – МЛИР» . mlir.llvm.org . Проверено 5 июля 2023 г.
- ^ Перейти обратно: а б «Интерфейсы — МЛИР» . mlir.llvm.org . Проверено 5 июля 2023 г.
- ^ Моисей, Уильям С.; Челини, Лоренцо; Чжао, Руйжэ; Зиненко, Александр (2021). Полигейст: повышение C до многогранного MLIR . 30-я Международная конференция по параллельным архитектурам и методам компиляции (PACT). стр. 45–59. дои : 10.1109/PACT52795.2021.00011 . ISBN 978-1-6654-4278-7 .
- ^ Агостини, Николя Бом; Керзель, Серена; Аматья, Винай; Тан, Ченг; Минуты, Марко; Кастеллана, Вито Джованни; Мансано, Джозеф; Каэли, Дэвид; Тумео, Антонино (30 октября 2022 г.). «Блок-компилятор на основе MLIR для проектирования на системном уровне и аппаратного ускорения» . Материалы 41-й Международной конференции IEEE/ACM по компьютерному проектированию . Ассоциация вычислительной техники. стр. 1–9. дои : 10.1145/3508352.3549424 . ISBN 978-1-4503-9217-4 .
- ^ Руйжэ, Чжао; Цзяньи, Ченг (2021). «Физма: многогранный синтез высокого уровня в MLIR». arXiv : 2103.15103 [ cs.PL ].
- ^ Маккаски, Александр; Нгуен, Тьен (октябрь 2021 г.). «Диалект MLIR для квантовых языков ассемблера». Международная конференция IEEE по квантовым вычислениям и инженерии (QCE) 2021 года . IEEE. стр. 255–264. arXiv : 2101.11365 . дои : 10.1109/QCE52317.2021.00043 . ISBN 978-1-6654-1691-7 . S2CID 231718965 .
- ^ Пак, Сунджэ; Сон, Усон; Нам, Сынхён; Ким, Хёнъю; Шин, Джунбум; Ли, Джунён (6 июня 2023 г.). «HEaaN.MLIR: оптимизирующий компилятор для быстрого кольцевого гомоморфного шифрования» . Труды ACM по языкам программирования . 7 (ПЛДИ): 196–220. дои : 10.1145/3591228 . ISSN 2475-1421 .
- ^ Говиндараджан, Санат; Мозес, Уильям С. «SyFER-MLIR: интеграция полностью гомоморфного шифрования в структуру компилятора MLIR» (PDF) .
- ^ «НАСЛЕДНИК: Промежуточное представление гомоморфного шифрования» . Гитхаб . Проверено 5 сентября 2023 г.
- ^ Джин, Дин; Берча, Джордж-Теодор; Ле, Тунг Д.; Чен, Тонг; Су, Гонг; Имаи, Харуки; Негиси, Ясуси; Леу, Ань; О'Брайен, Кевин; Кавачия, Киёкуни; Эйхенбергер, Александр Э. (2020). «Компиляция моделей нейронных сетей ONNX с использованием MLIR». arXiv : 2008.08272 [ cs.PL ].
- ^ Пиенаар, Жак (2020), MLIR в экосистеме TensorFlow , получено 6 июля 2023 г.
- ^ Ху, Пэнчао; Лу, Человек; Ван, Лей; Цзян, Гоюэ (2022). «ТПУ-MLIR: компилятор для ТПУ с использованием MLIR». arXiv : 2210.15016 [ cs.PL ].
- ^ Катель, Навдип; Хандельвал, Вивек; Бондугула, Удай (19 марта 2022 г.). «Генерация кода на основе MLIR для тензорных ядер графического процессора». Материалы 31-й Международной конференции ACM SIGPLAN по построению компиляторов . АКМ. стр. 117–128. дои : 10.1145/3497776.3517770 . ISBN 978-1-4503-9183-2 . S2CID 247522110 .
- ^ Бик, Аарт; Коанантакул, Пенпорн; Шпейсман, Татьяна; Василаче, Николя; Чжэн, Бися; Кьёлстад, Фредрик (31 декабря 2022 г.). «Поддержка компилятора для разреженных тензорных вычислений в MLIR» . Транзакции ACM по оптимизации архитектуры и кода . 19 (4): 1–25. arXiv : 2202.04305 . дои : 10.1145/3544559 . ISSN 1544-3566 . S2CID 246680261 .