10 июня 2015. Дмитрий Кашицын (HDsoft) дает обзор LLVM.
http://techtalks.nsu.ru
Видеозапись: https://plus.google.com/events/ctes98f7uhf19t5jlvlbk24dan4
В этом докладе мы кратко расскажем о таком звере, как LLVM, о котором много кто слышал, но немногие щупали. Что такое компилятор на самом деле? Чем LLVM отличается от других компиляторов? Как в LLVM происходит компиляция программы, как работают оптимизации? Наконец, какой путь проходит программа от разбора исходного текста до генерации исполняемого файла?
Лекция будет обзорной и не потребует от слушателей глубоких знаний теории компиляторов.
Лекция прочитана в рамках проекта Tech Talks @NSU – серии открытых лекций о разработке ПО и карьере в IT, проводимых в Новосибирском государственном университете.
Подробности: http://techtalks.nsu.ru
5. 5
Если серьезно
● Развитие computer science не стоит на месте
● Новые идеи — новые языки
● Новые языки — новые компиляторы
● Успех Rust и Go говорит сам за себя
6. 6
Мой уютный компилятор™
● Парсинг исходников
● Представление программы?
● Оптимизация всего и вся
● Целевая архитектура, наборы инструкций
● Аллокация и распределение регистров
● Интероперабельность — системные вызовы,
FFI, работа с библиотеками
● Генерация исполняемого файла, линковка
● Отладочная информация
7. 7
Структура команды x86 (OMG)
http://penberg.blogspot.ru/2010/04/short-introduction-to-x86-instruction.html
8. 8
Как угодить всем?
● Языки разные. Очень.
● Разное отношение к данным
● Разные модели памяти
● Разные целевые архитектуры
● Разные наборы инструкций
9. 9
Решение LLVM
● Запись программы в виде, не зависящем от
всего вышеперечисленного
● Промежуточное представление — IR код
● Обобщенная система команд
10. 10
Решение LLVM
● Запись программы в виде, не зависящем от
всего вышеперечисленного
● Промежуточное представление — IR код
● Обобщенная система команд
Внезапно: LLVM — это не VM o_O
11. 11
Разбор исходного текста
● LLVM считает, что AST уже есть
● Чтобы его получить, нужно провести
лексический и синтаксический анализ
● К счастью, это не надо делать вручную
● На помощь придут
– Flex/Bison
– ANTLR
– И другие
12. 12
Так видит программу человек
int gcd(int a, int b) {
while (b != 0) {
if (a > b)
a = a − b;
else
b = b − a;
}
return a;
}
13. 13
Так видит программу компилятор
condition
body
else-bodyif-body
while
variable
name: b
constant
value: 0
compare
op: ≠
branch
compare
op: >
assign
bin op
op: −
assign
bin op
op: −
statement
sequence
return
variable
name: a
variable
name: a
variable
name: a
variable
name: a
variable
name: a
variable
name: b
variable
name: b
variable
name: b
variable
name: b
condition
14. 14
Лексический анализ
● Первичный разбор текста программы
● Удаление лишнего шума
● Преобразование потока входных символов в
последовательность токенов
15. 15
Лексический анализатор flex
● Позволяет записать правила разбора в
текстовом, человеко-читаемом виде
● Регулярные выражения — это сила
● На выходе — токены
17. 17
Синтаксический анализатор bison
● На вход получает поток токенов от лексера и
файл описания грамматики
● На основании правил грамматики
производит разбор
● На выходе дает древовидное представление
программы с которым может работать
компилятор
20. 20
IR код
● Intermediate Representation
● Запись графа управления в привычном текстовом виде…
но с плюшками:
– Asm-подобный синтаксис, но с параметризованными
функциями
– Строгая типизация
– Структуры данных, указатели
– Const, Volatile модификаторы
– Нотация SSA
21. 21
Нотация SSA
● Static Single Assignment
● Переменных — нет о_О
● Все имена встречаются только единожды
● Функциональный стиль описания данных
● Императивный стиль описания операций
35. 35
Основные идеи оптимизаций
● Не делать то, что никому не нужно
● Не делать дважды то, что можно сделать
один раз (а лучше не делать вообще)
● Если можно получить тот же результат, но
меньшими усилиями — это нужно сделать
● Сокращение издержек на всех уровнях
36. 36
Виды оптимизаций
● Peephole оптимизации — буквально
«через замочную скважину». Локальные
оптимизации в пределах базового блока
● Внутрипроцедурные оптимизации
● Межпроцедурные оптимизации
● Оптимизации во время линковки
37. 37
Loop Invariant Code Motion (LICM)
● Вынос инвариантных значений за пределы цикла
● Перенос значений из тела цикла в базовый блок возврата
int sum_array(int* input, int length) {
int sum = 0;
for (int i = 0; i < length; ++i) {
if (sum > length * 1024 * 1024)
printf("note: limit exceeded");
sum += input[i];
}
return sum;
}
38. 38
Loop Invariant Code Motion (LICM)
● Вынос инвариантных значений за пределы цикла
● Перенос значений из тела цикла в базовый блок возврата
int sum_array(int* input, int length) {
int sum = 0;
for (int i = 0; i < length; ++i) {
if (sum > length * 1024 * 1024)
printf("note: limit exceeded");
sum += input[i];
}
return sum;
}
39. 39
Loop Invariant Code Motion (LICM)
● Вынос инвариантных значений за пределы цикла
● Перенос значений из тела цикла в базовый блок возврата
int sum_array(int* input, int length) {
int sum = 0;
const int len = length * 1024 * 1024;
for (int i = 0; i < length; ++i) {
if (sum > len)
printf("note: limit exceeded");
sum += input[i];
}
return sum;
}
40. 40
Common subexpression elimination (CSE)
● Удаление общих подвыражений
int sum_array(int* input, int length) {
int sum = 0;
int max = 0;
for (int i = 0; i < length; ++i) {
max = (input[i] > max) ? input[i] : max;
sum += input[i];
}
printf("max was %dn", max);
return sum;
}
41. 41
Common subexpression elimination (CSE)
● Удаление общих подвыражений
int sum_array(int* input, int length) {
int sum = 0;
int max = 0;
for (int i = 0; i < length; ++i) {
max = (input[i] > max) ? input[i] : max;
sum += input[i];
}
printf("max was %dn", max);
return sum;
}
42. 42
Common subexpression elimination (CSE)
● Удаление общих подвыражений
int sum_array(int* input, int length) {
int sum = 0;
int max = 0;
for (int i = 0; i < length; ++i) {
const int input_i = input[i];
max = (input_i > max) ? input_i : max;
sum += input_i;
}
printf("max was %dn", max);
return sum;
}
43. 43
Что можно почитать
● blog.llvm.org
● llvm.org/docs
● halt.habrahabr.ru/topics/
● llst.org