Построение компилятора на базе llvm — Павел Сычев

45

Upload: yandex

Post on 15-Jul-2015

280 views

Category:

Technology


8 download

TRANSCRIPT

Page 1: Построение компилятора на базе LLVM — Павел Сычев
Page 2: Построение компилятора на базе LLVM — Павел Сычев

Построение компилятора на базе LLVM

Сычев Павел ([email protected])

Page 3: Построение компилятора на базе LLVM — Павел Сычев

Чем будем заниматься

Построение компилятора на базе LLVM

Page 4: Построение компилятора на базе LLVM — Павел Сычев

Чем будем заниматься

〉Расскажу вкратце про компиляторы и их архитектуру

〉Обзор LLVM - что это такое и зачем он нам нужен

〉Написание компилятора простого Языка Программирования

4

Page 5: Построение компилятора на базе LLVM — Павел Сычев

Компилятор

Построение компилятора на базе LLVM

Page 6: Построение компилятора на базе LLVM — Павел Сычев

Компилятор

Вход: описание программы на исходном языке компилятора

Выход: описание той же программы, но на другом языке (зачастую в машинном коде или assembler-е)

6

Чтобы не переписывать компилятор “с нуля” под каждую целевую платформу - используется трехэтапная компиляция программ

Page 7: Построение компилятора на базе LLVM — Павел Сычев

Трехэтапная компиляция

7

Обработка и генерация дерева

разбораОптимизатор Генератор

машинного кода

Исходный код Машинный код

Frontend Backend

Page 8: Построение компилятора на базе LLVM — Павел Сычев

Трехэтапная компиляция

8

Haskell Frontend Оптимизатор

x86 Backend

Haskell

x86

PowerPC Backend

PowerPC

ARM BackendARM

C++ FrontendC++

Rust FrontendRust

Page 9: Построение компилятора на базе LLVM — Павел Сычев

LLVM-based compiler

9

Page 10: Построение компилятора на базе LLVM — Павел Сычев

Построение компилятора на базе LLVM

Обработка и генерация дерева разбора

Page 11: Построение компилятора на базе LLVM — Павел Сычев

11

1. Лексический анализ (производится Лексером)

2. Синтаксический анализ (производится Парсером)

Обработка и генерация дерева разбора

Page 12: Построение компилятора на базе LLVM — Павел Сычев

Лексер

12

Lexer:

>> (3 + 4.1) * a [«(», «3», «+», «4.1», «)», «*», «a»]

<< (LPAR) (INT, «3») (PLUS) (FLOAT, «4.1») (RPAR) (MUL) (ID, «a»)

1. Лексический анализ: 〉 Разбиение текста программы на токены

Можно использовать генераторы лексеров: Lex, Flex, JLex

Page 13: Построение компилятора на базе LLVM — Павел Сычев

Парсер

2. Синтаксический анализ:

〉 Преобразуем последовательность токенов в дерево разбора (AST) в соответствии с грамматикой языка.

13

Можно использовать генераторы парсеров: Yacc, Bison, JavaCC

*

a+

3 4.1

(LPAR) (INT, «3») (PLUS) (FLOAT, «4.1») (RPAR) (MUL) (ID, «a»)

Page 14: Построение компилятора на базе LLVM — Павел Сычев

LLVM

Построение компилятора на базе LLVM

Page 15: Построение компилятора на базе LLVM — Павел Сычев

LLVM

LLVM (Low Level Virtual Machine, compiler infrastructure)

〉Набор модулей и инструментов для разработки компиляторов

〉В основе LLVM лежит промежуточное представление (Intermediate Representation, IR) кода - типизированный трёхадресный код в SSA-форме

〉Реализует VM c RISC-подобными инструкциями и бесконечным количеством регистров

〉Есть API для написания frontend-а на С++ и OCaml

15

Page 16: Построение компилятора на базе LLVM — Павел Сычев

Возможности LLVM

〉Оптимизация промежуточного представления кода

〉Компилятор байт-кода в машинный код

〉 x86, x86-64, ARM, PowerPC, SPARC, MIPS, IA-64, Alpha

〉Интерпретация и JIT-компиляция байт-кода

〉 x86, x86_64, PowerPC, MIPS

〉Имеет множество frontend-ов: С, C++, Objective-C, Fortran, Ada, Haskell, Java, Python, Ruby, JavaScript, GLSL

16

Page 17: Построение компилятора на базе LLVM — Павел Сычев

Типы данных в LLVM

Построение компилятора на базе LLVM

Page 18: Построение компилятора на базе LLVM — Павел Сычев

Простые типы

Целые числа произвольной разрядности

〉 i1, i32, i17, i256, …

Числа с плавающей точкой

〉 float, double, …

Пустое значение

〉 void

18

Page 19: Построение компилятора на базе LLVM — Павел Сычев

Сложные типы

Указатели (тип*)

〉 i1*, i32*, float*, ...

Массивы ([число элементов х тип])

〉 [10 x float], [2 x i32]

Вектор (для упрощения SIMD операций)

〉 <4 x i32>

Структуры

〉 {i1, i32, double}

Функции:

〉i32 (i32, i32)

〉float ({float, float}, i1*)

19

Page 20: Построение компилятора на базе LLVM — Павел Сычев

Операции над стандартными типами в LLVM

Построение компилятора на базе LLVM

Page 21: Построение компилятора на базе LLVM — Павел Сычев

Операции

〉 Полный набор арифметических операций

〉 Тип операндов нужно всегда указывать явно

〉 Есть операции приведения типов (аналоги static_cast<> и reinterpret_cast<>)

21

; x = (a + b) * c - d / e

%tmp1 = add float %a, %b %tmp2 = mul float %tmp1, %c %tmp3 = fdiv float %d, %e %x = sub float %tmp2, %tmp3

Page 22: Построение компилятора на базе LLVM — Павел Сычев

Операции - 2

Инструкции для передачи управления Инструкции работы с памятью

〉 load, store, malloc, alloca

Работа с исключениями

〉 invoke, unwind

Работа с указателями

〉 getelementptr, extractvalue, insertvalue

22

Page 23: Построение компилятора на базе LLVM — Павел Сычев

Оптимизация кода в LLVM

Построение компилятора на базе LLVM

Page 24: Построение компилятора на базе LLVM — Павел Сычев

Общая схема работы алгоритма оптимизации

〉Ищем определенный шаблон в коде для преобразования

〉Проверяем, что преобразование ничего не сломает

〉Проводим преобразование

24

Page 25: Построение компилятора на базе LLVM — Павел Сычев

Простая оптимизация SimplifySubInst

〉Ищет выражения вида:

〉 X - X, X - 0, …

〉Проверяем, что преобразование ничего не сломает

〉 Если X - целое число, то данные выражения всегда можно оптимизировать

〉Проводим преобразование

〉 X - X = 0;

〉 X - 0 = X;

25

Page 26: Построение компилятора на базе LLVM — Павел Сычев

Встроенные алгоритмы оптимизации

〉Удаление неиспользуемого кода (dead code elimination)

〉Выделение одинаковых подвыражений (common subexpression elimination)

〉Распространение констант (constant propagation, condition propagation)

〉Inline-подстановка функций

〉Раскрутка и размыкание циклов, вынос инвариантов за пределы цикла

〉Оптимизация хвостовой рекурсии

26

Page 27: Построение компилятора на базе LLVM — Павел Сычев

Вспомогательные оптимизации

Преобразование может быть не только оптимизирующим, но и использоваться для анализа и инструментации

〉 Вывод графа потока управления в формате Graphviz

27

Page 28: Построение компилятора на базе LLVM — Павел Сычев

mkdir llvm_calc

Построение компилятора на базе LLVM

Page 29: Построение компилятора на базе LLVM — Павел Сычев

Постановка задачи

$> echo '2*2' | ./llvm_calc $< 4

$> echo '(1+1)*123/(6-3)' | ./llvm_calc $< 82

$> echo 'blah-blah' | ./llvm_calc $< Error: syntax error

Page 30: Построение компилятора на базе LLVM — Павел Сычев

Код лексера [Flex]

Построение компилятора на базе LLVM

Page 31: Построение компилятора на базе LLVM — Павел Сычев

Лексер [Flex]

31

%{

#include <string> #include "parser.hpp"

#define SAVE_TOKEN yylval.string = new std::string(yytext, yyleng) #define TOKEN(t) (yylval.token = t)

%}

%option noyywrap

Page 32: Построение компилятора на базе LLVM — Павел Сычев

32

%% [ \t\n] ; [0-9]+\.[0-9]* SAVE_TOKEN; return TDOUBLE; [0-9]+ SAVE_TOKEN; return TINT;

"(" return TOKEN(TLPAREN); ")" return TOKEN(TRPAREN);

"+" return TOKEN(TPLUS); "-" return TOKEN(TMINUS); "*" return TOKEN(TMUL); "/" return TOKEN(TDIV);

. printf("Unknown token!\n"); yyterminate(); %%

Page 33: Построение компилятора на базе LLVM — Павел Сычев

Код парсера [Bison]

Построение компилятора на базе LLVM

Page 34: Построение компилятора на базе LLVM — Павел Сычев

%% program : expr { ROOT_NODE = $1; };

numeric : TINT { $$ = new TInteger(atol($1->c_str())); delete $1; } | TDOUBLE { $$ = new TDouble(atof($1->c_str())); delete $1; } ;

expr : numeric | expr TMUL expr { $$ = new TBinaryOperator(*$1, $2, *$3); } | expr TDIV expr { $$ = new TBinaryOperator(*$1, $2, *$3); } | expr TPLUS expr { $$ = new TBinaryOperator(*$1, $2, *$3); } | expr TMINUS expr { $$ = new TBinaryOperator(*$1, $2, *$3); } | TLPAREN expr TRPAREN { $$ = $2; } ; %%

Page 35: Построение компилятора на базе LLVM — Павел Сычев

Код llvm-fronted [C++]

Построение компилятора на базе LLVM

Page 36: Построение компилятора на базе LLVM — Павел Сычев

LLVM AST

36

Value* TInteger::codeGen(TCodeGenContext& context) { return ConstantInt::get( Type::getInt64Ty(getGlobalContext()), value ); }

Value* TDouble::codeGen(TCodeGenContext& context) { return ConstantFP::get( Type::getDoubleTy(getGlobalContext()), value );

}

Page 37: Построение компилятора на базе LLVM — Павел Сычев

37

Value* TBinaryOperator::codeGen(TCodeGenContext& context) { Instruction::BinaryOps instr; switch (op) { case TPLUS: instr = Instruction::Add; break; case TMINUS: instr = Instruction::Sub; break; case TMUL: instr = Instruction::Mul; break; case TDIV: instr = Instruction::SDiv; break; default: return nullptr; }

return BinaryOperator::Create( instr, lhs.codeGen(context), rhs.codeGen(context), "op", context.mainBlock ); }

Page 38: Построение компилятора на базе LLVM — Павел Сычев

Генерация промежуточного представления кода

38

void TCodeGenContext::GenerateCode() { std::vector<Type*> argTypes; FunctionType* ftype = FunctionType::get( Type::getInt64Ty(getGlobalContext()), makeArrayRef(argTypes),false ); mainFunction = Function::Create(ftype, GlobalValue::InternalLinkage, "main", mainModule.get()); mainBlock = BasicBlock::Create(getGlobalContext(), "entry", mainFunction, 0); Value* result = root.codeGen(context); ReturnInst::Create(getGlobalContext(), result, mainBlock); }

Page 39: Построение компилятора на базе LLVM — Павел Сычев

39

$> echo '(1+1)*123/(6/2)' | ./llvm_calc

; ModuleID = 'module'

define internal i64 @main() { entry: %0 = add i64 1, 1 %1 = mul i64 %0, 123 %2 = sdiv i64 6, 2 %3 = sdiv i64 %1, %2 ret i64 %3 } Main returned: 82

Page 40: Построение компилятора на базе LLVM — Павел Сычев

Оптимизация генерируемого кода

40

void TCodeGenContext::GenerateCode() { ... ReturnInst::Create(getGlobalContext(), result, mainBlock); FunctionPassManager passManager(mainModule.get());

// Ликвидация общих подвыражений. passManager.add(createGVNPass());

passManager.doInitialization(); passManager.run(*mainFunction); }

Page 41: Построение компилятора на базе LLVM — Павел Сычев

41

$> echo '(1+1)*123/(6/2)' | ./llvm_calc

; ModuleID = 'module'

define internal i64 @main() { entry: ret i64 82 } Main returned: 82

Page 42: Построение компилятора на базе LLVM — Павел Сычев

Полезные ссылки по теме

Построение компилятора на базе LLVM

Page 43: Построение компилятора на базе LLVM — Павел Сычев

Полезные ссылки по теме

Lexer & Parser 〉http://ds9a.nl/lex-yacc/cvs/lex-yacc-howto.html 〉http://masters.donntu.org/2014/fknt/ianushkevych/ind/index.htm

LLVM Overview 〉http://habrahabr.ru/post/47878/ 〉http://www.aosabook.org/en/llvm.html

LLVM Tutorial 〉http://llvm.org/docs/tutorial/index.html 〉http://habrahabr.ru/post/119850/

HOWTO: Write your own toy compiler 〉http://gnuu.org/2009/09/18/writing-your-own-toy-compiler/all/1/ 〉https://github.com/lsegal/my_toy_compiler

43

Page 44: Построение компилятора на базе LLVM — Павел Сычев

Спасибо за внимание!

Page 45: Построение компилятора на базе LLVM — Павел Сычев

45

Павел Сычев

Разработчик

Контакты

[email protected]