Как написать JIT компилятор
description
Transcript of Как написать JIT компилятор
![Page 1: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/1.jpg)
Как написать JIT компилятор
Андрей АксеновSphinx
![Page 2: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/2.jpg)
Начнем… с конца
• Парсеры, bison, flex – это просто• Все варианты рантайма – это нетяжело• Байткод, дерево, и даже и машкод
• Свои DSL – это выгодно• Ничего про Java C2 не будет ;)• Времени на килодиссер маловато
![Page 3: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/3.jpg)
А теперь чуть подробнее
• Два слова про парсеры в целом• Два слова про flex, bison• Два слова про варианты рантайма• Два слова про DSL как потребность• Тупой пример имени Красной Нити• Строчный калькулятор!
![Page 4: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/4.jpg)
Делай-раз, про парсеры в целом(flex+bison 101+ftw)
![Page 5: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/5.jpg)
Калькулятор
• Eval ( “2+2” ) == ?• Eval ( “2+a*3” ) == ?• Eval ( “a = 2; b = 3; print 2+a*b;” ) == ?
![Page 6: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/6.jpg)
Калькулятор
• Eval ( “2+2” ) == ?• Eval ( “2+a*3” ) == ?• Eval ( “a = 2; b = 3; print 2+a*b;” ) == ?
• Для простоты ограничимся 1 формулой• “Сложный” случай тупо... нуднее
![Page 7: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/7.jpg)
Мой первый калькулятор!
const char * sExpr = "2+a*3";
void Parse ( const char * p ){ while ( isspace(*p) ) p++; const char * sTok = p; if ( isdigit(*p) )
...
![Page 8: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/8.jpg)
Problem?
• Работает отлично, между прочим!• Но – для очень простых парсеров• option = value1 [ , value2 [ , value3 [ … ] ] ]• 2 + a*3 - sin(b^4<<5)• SELECT a, b*2 FROM myindex …
![Page 9: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/9.jpg)
Problem?
• Для просто простых – уже сложновато• option = value1 [ , value2 [ , value3 [ … ] ] ]• 2 + a*3 - sin(b^4<<5)• SELECT a, b*2 FROM myindex …
• Получается много глупого кода• (Очень) тяжело поддерживать
![Page 10: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/10.jpg)
Solution!
• Решение, понятно, давно придумано
![Page 11: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/11.jpg)
Разделяем и властвуем!
• Было – все перепутано• Стало – три отдельные фазы• Лексический разбор• Синтаксический разбор• Действия
![Page 12: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/12.jpg)
![Page 13: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/13.jpg)
Лексический разбор
• Вход – “2+a*3”• Выход – отдельные токены• 2, +, a, *, 3
• Токены – типизированные полиморфы!• type = T_INT, value = 2• type = T_OP, op = +
![Page 14: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/14.jpg)
Синтаксический разбор
• Вход – поток токенов• Выход – • Или успешное натягивание потока на
заданные правила синтаксиса• Или ошибка разбора
• Заодно – исполнение действий!
![Page 15: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/15.jpg)
Действия
• Просто код (вашей) программы• Вход – аргументы совпавшего правила• Выход – желанные сайд-эффекты
![Page 16: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/16.jpg)
Пример, даешь калькулятор!
• Лексер, смертельно упрощенный:%%“SIN” { return T_SIN; }[0-9]+ { return T_NUMBER; }[a-zA-Z]+ { return T_VARIABLE; }%%
![Page 17: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/17.jpg)
Пример, даешь калькулятор!
• Лексер, добавляем операторы:“+” { return ’+’; }“-” { return ’-’; }“/” { return ’/’; }“*” { return ’*’; }
![Page 18: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/18.jpg)
Пример, даешь калькулятор!
• Лексер, исправляем переменные:[a-zA-Z]+ { return T_VARIABLE; }[a-zA-Z][a-zA-Z0-9]+ { return T_VARIABLE; }
![Page 19: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/19.jpg)
Пример, даешь калькулятор!
• Парсер, объявляем токены:%token T_NUMBER%token T_VARIABLE
![Page 20: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/20.jpg)
Пример, даешь калькулятор!
• Парсер, объявляем приоритеты:%left '+' '-'%left '*' '/'%%
![Page 21: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/21.jpg)
Пример, даешь калькулятор!
• Парсер, самое главное: правила!expr: item
| expr '+' expr | expr '-' expr| expr '*' expr | expr '/' expr| T_SIN '(' expr ')'| '(' expr ')' ;
![Page 22: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/22.jpg)
Пример, даешь калькулятор!
• Парсер, top-down уточнение:item:
T_NUMBER| T_VARIABLE;
%%
![Page 23: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/23.jpg)
И вот весь наш калькулятор
%%[0-9]+ { return T_NUMBER; }[a-zA-Z]+ { return T_VARIABLE; }“+” { return ‘+’; }“-” { return ‘-’; }“/” { return ‘/’; }“*” { return ‘*’; }%%
%token TOKEN_NUMBER%token TOKEN_VARIABLE%left '+' '-‘%left '*' '/‘%%expr:
item| expr '+' expr| expr '-' expr| expr '*' expr| expr '/' expr| '(' expr ')‘;
item:TOKEN_NUMBER| TOKEN_VARIABLE;
%%
![Page 24: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/24.jpg)
Что мы пропустили? ;)
![Page 25: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/25.jpg)
Сделать что-нибудь!!!
• Это был скелет
• Лексер: типа мало, надо значение• Парсер: правила мало, надо действие
![Page 26: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/26.jpg)
Что-нибудь в лексере
• Тип токена YYSTYPE, по умолчанию int• Не путать с номером токена, всегда int!• Либо %union, %token <field> etc в парсере• Либо руками typedef снаружи
• Совсем рабочий кусок лексера:[0-9]+ { lvalp->m_iValue = atoi ( yytext );
return TOKEN_NUMBER; }
![Page 27: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/27.jpg)
Что-нибудь в парсере
• Правила как бы трансформируют “токены” в другие “токены”• На самом деле, "термы"• Правила как в BNF• Каждая селедка - рыба!
• На выходе – последний мега-терм
![Page 28: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/28.jpg)
Что-нибудь в парсере
%token <m_iValue> T_INT%token <m_sName> T_VARIABLE%token <m_pExpr> expr…
| expr ‘+’ expr {$$ = CreateOpNode ( OP_ADD, $1, $2 ); }
| T_INT { $$ = CreateConstNode ( $1 ); }
![Page 29: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/29.jpg)
Боевой вариант, столько же LOC
%%[0-9]+ { …; return T_NUMBER; }[a-zA-Z]+ { …; return T_VARIABLE; }“+” { return ‘+’; }“-” { return ‘-’; }“/” { return ‘/’; }“*” { return ‘*’; }%%
%token …%left '+' '-‘%left '*' '/‘%%expr:
item { … }| expr '+' expr { … }| expr '-' expr { … }| expr '*' expr { … }| expr '/' expr { … }| '(' expr ')' { …};
item:TOKEN_NUMBER { …}| TOKEN_VARIABLE { …};
%%
![Page 30: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/30.jpg)
Итого-раз
![Page 31: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/31.jpg)
Не так страшен yacc
• Парсер вручную – да, адово сложно (*)• Парсер автоматом – просто!!!• Автоматизируем 2 из 3 штук• Нетяжело сделать лексер на flex• Нетяжело сделать парсер на bison• И останется сделать только мясо
![Page 32: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/32.jpg)
Почему именно flex/bison?
• Классика (lex/yacc * GNU = flex/bison)• “Любу знают все”• Все еще покрывает кучу задач• Если начнет жать, antlr, lemon, accent…
• Учитесь, оно того стоит• Лучше день потерять!
![Page 33: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/33.jpg)
Делай-два, про рантайм
![Page 34: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/34.jpg)
![Page 35: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/35.jpg)
Варианты рантайма
• Определяют “как будем считать”• Определяют “во что будем разбирать”• Иногда рантайма совсем нет• Иногда рантайм таки есть• Как сделать?• Байткод (оно же VM), дерево, машкод
![Page 36: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/36.jpg)
![Page 37: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/37.jpg)
Когда рантайма нет?
• Типично, при разовых акциях• Например, разобрать конфиг• Например, один раз (!) вычислить
• Делаем дела прямо в action-ах• Результат в момент окончания разбора
![Page 38: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/38.jpg)
Когда рантайма нет
// parserexpr:expr ‘+’ expr { $$ = $1 + $3; }| expr ‘-’ expr { $$ = $1 - $3; }
...// applicationfloat Result = yyparse();
![Page 39: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/39.jpg)
![Page 40: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/40.jpg)
Байткод, общее
• В чем отличие от машкода?• Тоже набор инструкций, однако• Упрощенный• Портабельный• Чаще стековый (JVM, NET)
• Привет, обратная Польская нотация!
![Page 41: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/41.jpg)
Байткод, пример данных
• Вход• 2+3*a
• (Вариант) разбора• 3 a * 2 +
• Байткод• load_const 3; load_var a; op_multiply; …
![Page 42: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/42.jpg)
Байткод, пример парсера
// parserexpr:expr ‘+’ expr {
dCodes.Append ( $1 );dCodes.Append ( $3 );dCodes.Append ( OP_ADD );
}| ...
![Page 43: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/43.jpg)
Байткод, пример VMVector<float> dStack;for ( int i=0; i<dCodes.Length(); i++ ) switch ( dCodes[i].m_Opcode ){ case LOAD_CONST: dStack.Push ( dCodes[i].m_fValue ); break; case LOAD_VAR: dStack.Push ( GetVar ( dCodes[i].m_sName ) ); break;
case OP_MUL: dStack.Push ( dStack.Pop() * dStack.Pop() ); break;
...
![Page 44: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/44.jpg)
Байткод, снова общее
• Радость: очень понятная модель (RPN)• Радость: упрощен и портабелен• Радость: простые вирт-машинки• Плохо: не влобно ложится на регистры• Плохо: лучше отдельно оптимизить
![Page 45: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/45.jpg)
![Page 46: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/46.jpg)
Дерево, общее
• Abstract Syntax Tree• Почти оно, с “небольшим” довеском• AST – только представление• Нужно – еще и исполнение
• Итого, деревьев по факту может оказаться и два
![Page 47: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/47.jpg)
Дерево, пример парсера
// parserexpr:expr ‘+’ expr {
$$ = new NodeAdd ( $1, $3 );}| ...
![Page 48: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/48.jpg)
Дерево, пример “VM”virtual float NodeConst::Eval(){ return m_Value;}
virtual float iNodeAdd::Eval(){ return m_pArg1->Eval() + m_pArg2->Eval();}
// ...
![Page 49: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/49.jpg)
Дерево, снова общее
• Радость: тоже понятная модель (AST)• Радость: изоморфно (!) байт-коду• Радость: удобно трансформировать• Непонятно: операции размазаны• Плохо: обходы дерева…• Радость для калькулятора: БЫСТРЕЕ
![Page 50: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/50.jpg)
![Page 51: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/51.jpg)
(gdb) disas /m main Dump of assembler code for function main: 5 { 0x08048330 <+0>: push %ebp 0x08048331 <+1>: mov %esp,%ebp 0x08048333 <+3>: sub $0x8,%esp 0x08048336 <+6>: and $0xfffffff0,%esp 0x08048339 <+9>: sub $0x10,%esp 6 printf ("Hello.\n"); => 0x0804833c <+12>: movl $0x8048440,(%esp) 0x08048343 <+19>: call 0x8048284 <puts@plt> 7 return 0; 8 } 0x08048348 <+24>: mov $0x0,%eax 0x0804834d <+29>: leave 0x0804834e <+30>: ret End of assembler dump.
![Page 52: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/52.jpg)
Машкод, общее
• Радость: родной для железа• Радость: предельная скорость• Плохо: сложнее (?) генерировать• Плохо: регистровая машина• Плохо: нужны (?) оптимизации
![Page 53: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/53.jpg)
Машкод, фокусы
• Про калькулятор и не только• Раз. FPU и есть стековая машина ;)• Два. Конечный стек легко и явно
мапится на регистры• Три. Бесконечный стек чуть сложнее
мапится на регистры и память
![Page 54: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/54.jpg)
Машкод, пример генератора// assemble mulif ( tNode.m_iToken=='*' ){
CreateNative ( tNode.m_iLeft, uAttrType, pRes );
CreateNative ( tNode.m_iRight, uAttrType, pRes );
*pRes->m_pCurCode++ = 0xDE; // fmulp st(1),st(0)
*pRes->m_pCurCode++ = 0xC9;return;
}
![Page 55: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/55.jpg)
Машкод, а исполнять-то как?!
• В три строки!• Неизбежное, указатель на функциюtypedef float ( * NativeEval_fn )
( void * pArg );NativeEval_fn pFn =
( NativeEval_fn ) m_pCode;return pFn ( pArg );
![Page 56: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/56.jpg)
Машкод, а исполнять-то как-2?!
• Еще бывает DEP и его братья ;)• Linuxmprotect ( addr, len, PROT_EXEC );
• WindowsVirtualProtect ( …, PAGE_EXECUTE );
• До кучи: еще бывает dlopen, dlclose…
![Page 57: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/57.jpg)
Итого про машкод
• Разрушаем мифы!• Прототип про калькулятор поверх AST• 150 строк, 1 вечер• Без учета AST (ну, второй вечер)
• 95% производительности натива (?!)• Портабельность? x86, SSE2+ победили
![Page 58: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/58.jpg)
Итого про рантайм
• Разрушаем мифы!• Вот, три классических варианта• Вот, каждый вполне применим• Вот, каждый вполне нетяжел• Никому не верьте, особенно мне ;)
![Page 59: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/59.jpg)
Делай-три, про радость DSL
![Page 60: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/60.jpg)
…радость в DSL есть.
![Page 61: Как написать JIT компилятор](https://reader035.fdocuments.net/reader035/viewer/2022062217/5681500b550346895dbde751/html5/thumbnails/61.jpg)
Я НИ ФИГА НЕ ПОНЯЛ ТОЧКА ЖПГ!