Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
LLVM: Как приручить дракона?
Дмитрий Каш цын, HDSoftии
Как работает компилятор
1. Прочитать текст программы из файла
2. Записать исполняемый файл
Чуть детальнее
● Разбор лексем и синтаксический анализ
● Построение AST программы
● Преобразования (магия)
● Генерация объ...
4
Но зачем?!*
* http://www.linux.org.ru/gallery/workplaces/10931314
5
Если серьезно
● Развитие computer science не стоит на месте
● Новые идеи — новые языки
● Новые языки — новые компиляторы...
6
Мой уютный компилятор™
● Парсинг исходников
● Представление программы?
● Оптимизация всего и вся
● Целевая архитектура, ...
7
Структура команды x86 (OMG)
http://penberg.blogspot.ru/2010/04/short-introduction-to-x86-instruction.html
8
Как угодить всем?
● Языки разные. Очень.
● Разное отношение к данным
● Разные модели памяти
● Разные целевые архитектуры...
9
Решение LLVM
● Запись программы в виде, не зависящем от
всего вышеперечисленного
● Промежуточное представление — IR код
...
10
Решение LLVM
● Запись программы в виде, не зависящем от
всего вышеперечисленного
● Промежуточное представление — IR код...
11
Разбор исходного текста
● LLVM считает, что AST уже есть
● Чтобы его получить, нужно провести
лексический и синтаксичес...
12
Так видит программу человек
int gcd(int a, int b) {
while (b != 0) {
if (a > b)
a = a − b;
else
b = b − a;
}
return a;
}
13
Так видит программу компилятор
condition
body
else-bodyif-body
while
variable
name: b
constant
value: 0
compare
op: ≠
b...
14
Лексический анализ
● Первичный разбор текста программы
● Удаление лишнего шума
● Преобразование потока входных символов...
15
Лексический анализатор flex
● Позволяет записать правила разбора в
текстовом, человеко-читаемом виде
● Регулярные выраж...
16
Входной файл для flex (огрызок)
whitespace [ rnt]*
number [0-9]+
identifier [a-zA-Z]+
{whitespace} { /* ignore whitespa...
17
Синтаксический анализатор bison
● На вход получает поток токенов от лексера и
файл описания грамматики
● На основании п...
18
Входной файл для bison (огрызок)
input:
| input line;
line:
'n'
| exp 'n' { complete("result is %gn", $1); };
exp:
TOKE...
19
IR код
● Intermediate Representation
● Запись графа управления в привычном текстовом виде…
20
IR код
● Intermediate Representation
● Запись графа управления в привычном текстовом виде…
но с плюшками:
– Asm-подобны...
21
Нотация SSA
● Static Single Assignment
● Переменных — нет о_О
● Все имена встречаются только единожды
● Функциональный ...
22
Что хорошего в SSA?
X ← 1
X ← 2
Y ← X
23
Что хорошего в SSA?
X ← 1
X ← 2
Y ← X
X1←1
X2←2
Y1←X2
24
Что хорошего в SSA?
X ← 1
X ← 2
Y ← X
X1←1 (RIP)
X2←2
Y1←X2
25
Что хорошего в SSA?
X ← 1
X ← 2
Y ← X
X1←1 (RIP)
X2←2 (RIP)
Y1←2
26
Граф потока управления*
● Control flow graph (CFG)
● Узлы — базовые блоки
● Ребра — инструкции
переходов
● Базовый блок...
27
Циклы и ветвления в SSA
28
Циклы и ветвления в SSA
29
Циклы и ветвления в SSA
30
Подсчет суммы массива
int sum_array(int* input, int length) {
int sum = 0;
for (int i = 0; i < length; ++i)
sum += inpu...
31
Листинг IR кода
1 ; Function Attrs: nounwind readonly
2 define i32 @sum_array(int*, int)(i32* nocapture readonly %input...
32
Результат clang -O1 -m32
sum_array(int const*, int):
mov ecx, dword ptr [esp + 8]
xor eax, eax
test ecx, ecx
jle .LBB0_...
33
Результат clang -O1 (x64)
sum_array(int const*, int):
xor eax, eax
test esi, esi
jle .LBB0_2
.LBB0_1: # %.lr.ph
add eax...
34
Оптимизации
● Наблюдаемое поведение
● Точки следования
● Эквивалентные преобразования
35
Основные идеи оптимизаций
● Не делать то, что никому не нужно
● Не делать дважды то, что можно сделать
один раз (а лучш...
36
Виды оптимизаций
● Peephole оптимизации — буквально
«через замочную скважину». Локальные
оптимизации в пределах базовог...
37
Loop Invariant Code Motion (LICM)
● Вынос инвариантных значений за пределы цикла
● Перенос значений из тела цикла в баз...
38
Loop Invariant Code Motion (LICM)
● Вынос инвариантных значений за пределы цикла
● Перенос значений из тела цикла в баз...
39
Loop Invariant Code Motion (LICM)
● Вынос инвариантных значений за пределы цикла
● Перенос значений из тела цикла в баз...
40
Common subexpression elimination (CSE)
● Удаление общих подвыражений
int sum_array(int* input, int length) {
int sum = ...
41
Common subexpression elimination (CSE)
● Удаление общих подвыражений
int sum_array(int* input, int length) {
int sum = ...
42
Common subexpression elimination (CSE)
● Удаление общих подвыражений
int sum_array(int* input, int length) {
int sum = ...
43
Что можно почитать
● blog.llvm.org
● llvm.org/docs
● halt.habrahabr.ru/topics/
● llst.org
Спасибо за внимание!
Upcoming SlideShare
Loading in …5
×

Как приручить дракона: введение в LLVM

157 views

Published on

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

Published in: Technology
  • Be the first to comment

  • Be the first to like this

Как приручить дракона: введение в LLVM

  1. 1. LLVM: Как приручить дракона? Дмитрий Каш цын, HDSoftии
  2. 2. Как работает компилятор 1. Прочитать текст программы из файла 2. Записать исполняемый файл
  3. 3. Чуть детальнее ● Разбор лексем и синтаксический анализ ● Построение AST программы ● Преобразования (магия) ● Генерация объектного файла ● Линковка исполняемого файла
  4. 4. 4 Но зачем?!* * http://www.linux.org.ru/gallery/workplaces/10931314
  5. 5. 5 Если серьезно ● Развитие computer science не стоит на месте ● Новые идеи — новые языки ● Новые языки — новые компиляторы ● Успех Rust и Go говорит сам за себя
  6. 6. 6 Мой уютный компилятор™ ● Парсинг исходников ● Представление программы? ● Оптимизация всего и вся ● Целевая архитектура, наборы инструкций ● Аллокация и распределение регистров ● Интероперабельность — системные вызовы, FFI, работа с библиотеками ● Генерация исполняемого файла, линковка ● Отладочная информация
  7. 7. 7 Структура команды x86 (OMG) http://penberg.blogspot.ru/2010/04/short-introduction-to-x86-instruction.html
  8. 8. 8 Как угодить всем? ● Языки разные. Очень. ● Разное отношение к данным ● Разные модели памяти ● Разные целевые архитектуры ● Разные наборы инструкций
  9. 9. 9 Решение LLVM ● Запись программы в виде, не зависящем от всего вышеперечисленного ● Промежуточное представление — IR код ● Обобщенная система команд
  10. 10. 10 Решение LLVM ● Запись программы в виде, не зависящем от всего вышеперечисленного ● Промежуточное представление — IR код ● Обобщенная система команд Внезапно: LLVM — это не VM o_O
  11. 11. 11 Разбор исходного текста ● LLVM считает, что AST уже есть ● Чтобы его получить, нужно провести лексический и синтаксический анализ ● К счастью, это не надо делать вручную ● На помощь придут – Flex/Bison – ANTLR – И другие
  12. 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. 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. 14 Лексический анализ ● Первичный разбор текста программы ● Удаление лишнего шума ● Преобразование потока входных символов в последовательность токенов
  15. 15. 15 Лексический анализатор flex ● Позволяет записать правила разбора в текстовом, человеко-читаемом виде ● Регулярные выражения — это сила ● На выходе — токены
  16. 16. 16 Входной файл для flex (огрызок) whitespace [ rnt]* number [0-9]+ identifier [a-zA-Z]+ {whitespace} { /* ignore whitespaces */ } {number} { sscanf(yytext, "%d", &yylval->value); return TOKEN_NUMBER; } "+" { return PLUS; } "-" { return MINUS; } "(" { return LPAREN; } ")" { return RPAREN; } ";" { return SEMICOLON; } "," { return COMMA; } "=" { return EQ; } "!=" { return NEQ; } ">" { return GTR; } "if" { return IFSYM; } "while" { return WHILESYM; }
  17. 17. 17 Синтаксический анализатор bison ● На вход получает поток токенов от лексера и файл описания грамматики ● На основании правил грамматики производит разбор ● На выходе дает древовидное представление программы с которым может работать компилятор
  18. 18. 18 Входной файл для bison (огрызок) input: | input line; line: 'n' | exp 'n' { complete("result is %gn", $1); }; exp: TOKEN_NUMBER { $$ = $1; } | exp '+' exp { $$ = $1 + $3; } | exp '-' exp { $$ = $1 - $3; } | exp '/' exp { $$ = $1 / $3; } | exp '*' exp { $$ = $1 * $3; } | '(' exp ')' { $$ = $2; }; Позволяет разбирать арифметические выражения вида (2 + 3 * 4) * 5
  19. 19. 19 IR код ● Intermediate Representation ● Запись графа управления в привычном текстовом виде…
  20. 20. 20 IR код ● Intermediate Representation ● Запись графа управления в привычном текстовом виде… но с плюшками: – Asm-подобный синтаксис, но с параметризованными функциями – Строгая типизация – Структуры данных, указатели – Const, Volatile модификаторы – Нотация SSA
  21. 21. 21 Нотация SSA ● Static Single Assignment ● Переменных — нет о_О ● Все имена встречаются только единожды ● Функциональный стиль описания данных ● Императивный стиль описания операций
  22. 22. 22 Что хорошего в SSA? X ← 1 X ← 2 Y ← X
  23. 23. 23 Что хорошего в SSA? X ← 1 X ← 2 Y ← X X1←1 X2←2 Y1←X2
  24. 24. 24 Что хорошего в SSA? X ← 1 X ← 2 Y ← X X1←1 (RIP) X2←2 Y1←X2
  25. 25. 25 Что хорошего в SSA? X ← 1 X ← 2 Y ← X X1←1 (RIP) X2←2 (RIP) Y1←2
  26. 26. 26 Граф потока управления* ● Control flow graph (CFG) ● Узлы — базовые блоки ● Ребра — инструкции переходов ● Базовый блок — линейный участок кода (a) (c) (b) (d) * https://en.wikipedia.org/wiki/Control_flow_graph
  27. 27. 27 Циклы и ветвления в SSA
  28. 28. 28 Циклы и ветвления в SSA
  29. 29. 29 Циклы и ветвления в SSA
  30. 30. 30 Подсчет суммы массива int sum_array(int* input, int length) { int sum = 0; for (int i = 0; i < length; ++i) sum += input[i]; return sum; }
  31. 31. 31 Листинг IR кода 1 ; Function Attrs: nounwind readonly 2 define i32 @sum_array(int*, int)(i32* nocapture readonly %input, i32 %length) #0 { 3 %1 = icmp sgt i32 %length, 0 ; а есть вообще что суммировать? 4 br i1 %1, label %.lr.ph, label %._crit_edge 5 ._crit_edge: 6 %sum.0.lcssa = phi i32 [ 0, %0 ], [ %4, %.lr.ph ] 7 ret i32 %sum.0.lcssa ; возврат результата 8 .lr.ph: 9 %i.02 = phi i32 [ %5, %.lr.ph ], [ 0, %0 ] 10 %sum.01 = phi i32 [ %4, %.lr.ph ], [ 0, %0 ] 11 ; вычисление адреса текущего элемента в массиве и его загрузка в регистр 12 %2 = getelementptr inbounds i32, i32* %input, i32 %i.02 13 %3 = load i32, i32* %2, align 4 14 ; аккумулирование суммы и инкремент индекса 15 %4 = add nsw i32 %3, %sum.01 ; новое значение sum 16 %5 = add nuw nsw i32 %i.02, 1 ; новое значение i 17 ; условие выхода 18 %exitcond = icmp eq i32 %5, %length 19 ; проверка условия выхода и переход 20 br i1 %exitcond, label %._crit_edge, label %.lr.ph 21 }
  32. 32. 32 Результат clang -O1 -m32 sum_array(int const*, int): mov ecx, dword ptr [esp + 8] xor eax, eax test ecx, ecx jle .LBB0_3 mov edx, dword ptr [esp + 4] .LBB0_2: # %.lr.ph add eax, dword ptr [edx] add edx, 4 dec ecx jne .LBB0_2 .LBB0_3: # %._crit_edge ret
  33. 33. 33 Результат clang -O1 (x64) sum_array(int const*, int): xor eax, eax test esi, esi jle .LBB0_2 .LBB0_1: # %.lr.ph add eax, dword ptr [rdi] add rdi, 4 dec esi jne .LBB0_1 .LBB0_2: # %._crit_edge ret
  34. 34. 34 Оптимизации ● Наблюдаемое поведение ● Точки следования ● Эквивалентные преобразования
  35. 35. 35 Основные идеи оптимизаций ● Не делать то, что никому не нужно ● Не делать дважды то, что можно сделать один раз (а лучше не делать вообще) ● Если можно получить тот же результат, но меньшими усилиями — это нужно сделать ● Сокращение издержек на всех уровнях
  36. 36. 36 Виды оптимизаций ● Peephole оптимизации — буквально «через замочную скважину». Локальные оптимизации в пределах базового блока ● Внутрипроцедурные оптимизации ● Межпроцедурные оптимизации ● Оптимизации во время линковки
  37. 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. 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. 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. 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. 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. 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. 43 Что можно почитать ● blog.llvm.org ● llvm.org/docs ● halt.habrahabr.ru/topics/ ● llst.org
  44. 44. Спасибо за внимание!

×