300 likes | 603 Views
Системы программирования (СП) Создание СП для новых архитектур. Владиславлев Виктор. Agenda. Содержание. Системы программирования Структура компилятора Препроцессор Компилятор Ассемблер Линковщик Динамический Линковщик Прочие системные утилиты Библиотеки Создание/Портирование СП
E N D
Системы программирования (СП) Создание СП для новых архитектур Владиславлев Виктор
Agenda Содержание Системы программирования Структура компилятора Препроцессор Компилятор Ассемблер Линковщик Динамический Линковщик Прочие системные утилиты Библиотеки Создание/Портирование СП Machine Description Двоичный интерфейс приложения Библиотеки Симулятор Средства отладки Средства анализа производительности Форматы исполняемых файлов Форматы отладочной информации
Toolchain Системы программирования Toolchain – набор средств разработки программ Компилятор Бинарные утилиты Библиотеки Средства отладки и профилирования И проч. (редакторы, навигаторы по коду, системы автодокументации, верификаторы и т.д.) Примеры: GNU toolchain – Unix/Linux/Windows на большинстве архитектур Microsoft Visual Studio – Windows на x86 ICC – toolchain от компании Intel – Windows/Linux на x86 и IA64 Code Warrior – toochain для встроенных систем Xcode – toochain для Mac OS X и iOS Можно разделить Нативные системы (host = target) Кросс-системы (host != target)
Compiler structure Структура компилятора ядро компилятора .c .cpp .f77 ... .c .cpp .F ... High-Level IR Low-Level IR asm .o .obj .out .exe High-Level IR Low-Level IR High-Level IR Low-Level IR 1 2 4 5 6 Компилятор - переводит исходный код программы (написанные на языке высокого уровня) в эквивалентный код на языке целевой платформы • 1.Препроцессор • 2.Front-End • 3.Оптимизации • 4.Кодогенератор • 5.Ассемблер • 6.Линкер • ++ Можно проследить запуски фаз компиляции (опция ‘--verbose’ в GCC)
Preprocessing Триграф значение /??/ * */ # /* */ defi\ ne FO\ O 10\ 20 #define FOO 1020 Препроцессирование ??( [ ??) ] ??< { ??> } ??= # ??/ \ ??' ^ ??! | ??- ~ a = (i++) + j; Инициальная обработка Разбиение на строки Замена триграфов Объединение строк Замена комментариев Рабиение на токены (слева направо, жадная) Замена диграфов (в С++) Собственно препроцессирование Подключение файлов (#include) Макроподстановки (#define/undef/##) Условная компиляция (#ifdef/if/else/elif/endif) Управление строками (#line) Диагностика (#error/warning) ++ Часто полезно получить препроцессированный код (опция ‘-E’ в GCC) a = i+++j; a = i + (++j);
Preprocessing Триграф значение /??/ * */ # /* */ defi\ ne FO\ O 10\ 20 #define FOO 1020 Препроцессирование ??( [ ??) ] ??< { ??> } ??= # ??/ \ ??' ^ ??! | ??- ~ a = (i++) + j; Инициальная обработка Разбиение на строки Замена триграфов Объединение строк Замена комментариев Рабиение на токены (слева направо, жадная) Замена диграфов (в С++) Собственно препроцессирование Подключение файлов (#include) Макроподстановки (#define/undef/##) Условная компиляция (#ifdef/if/else/elif/endif) Управление строками (#line) Диагностика (#error/warning) ++ Часто полезно получить препроцессированный код (опция ‘-E’ в GCC) a = i+++j; a = i + (++j);
Compiler собственно Компилятор Компилятор обрабатывает единицу трансляции(translating unit TU, compilation unit). В С/С++ это файл после препроцессирования. В FORTRAN – один файл может содержать несколько TU. Компилятор включает: Компилятор переднего плана (Frontend) – строит по исходному коду промежуточное представление (Intermediate Representation – IR, или IL); включает Лексический анализатор Синтаксический анализатор Семантический анализатор Фазы Анализа и Оптимизаций представления их много (Основная часть курса), но можно и уменьшить: разные уровни оптимизаций Фазы Распределение регистров и кодогенерация Результат работы компилятора – язык ассемблера (ассемблер, машинный язык) целевой архитектуры ++ Можно проследить результат работы компилятора после каждой фазы (опции ‘--dump-tree-all --dump-rtl-all’ в GCC)
Code transformation int f(int b, int c) { int a1 = b*5 - c; int a2 = -8*b + 2*c; return 2*a1 + a2; } D.26 = b * 5; a1 = D.26 - c; 27 = b * -4; D.28 = D.27 + c; a2 = D.28 * 2; D.20 = a1 * 2; D.29 = D.20 + a2; return D.29; трансформация Кода a1 -8 set (reg:SI 62) (ashift:SI (reg/v:SI 60 [ b ]) (const_int 1 [0x1]); set (reg/i:SI 0 ax) (reg:SI 62); Source code AST High-level IR Low-level IR assembler a2 .globl f .type f, @function f: .LFB0: .cfi_startproc leal (%rdi,%rdi), %eax ret .cfi_endproc a1 a2 ret ++ Бывает полезно посмотреть ассемблерный код (опция ‘-S’ в GCC)
Assembler Ассемблер Ассемблер как программа переводит язык ассемблера в код целевой архитектуры, сохраняемый в исполняемом формате Для символьных ссылок – резервирует место, подстановку реальных адресов осуществляет линкер КАК ПРАВИЛО, ассемблер – тривиален (парсинг, упаковщик). Исключение: IA-64 (Itanium) (виртуальные регистры, шаблоны, скобки параллельности) Язык ассемблера Имеет типы (наследованные от архитектуре), комментарии, объекты и пр. характерезуется тривиальным синтаксисом может быть не стандартизирован даже в рамках одной платформы movl %ebx, %eax AT&T mov eax, ebx Intel Регистры начинаются с % b, w, l, q – размер операнда Имена регистров зарезервированы Сначала dst, потом src
Linker Линковщик Дефиниция – полное определение сущности (глобала или функции) Декларация – лишь обещание того, что где-то есть дефиниция Объектный файл содержит дефиниции функций и глобалов В коде есть ссылки на декларированные глобалы и/или функции Основная задача линковщика (линкера) – реализовать чужие обещания Проблемы: Никого не нашли – ошибка! Нескольких – (duplicate definitions): С++: ‘one definition rule’ С: ‘tentative definition’ для неинициализированных глобалов FORTRAN: ‘common model’ – в каждой TU свой COMMON блок, своего размера Инициализированный глобал Предварительное определение Декларация Дефиниция Переменная на стеке Указывает на память в куче int G1 = 1; int G2; extern int G3; extern int f( int*); int g( int n) { int* h = malloc(n*sizeof(int)); f( h); return h[ G2 ]; } file code data bss memory code data bss: 000...00 heap stack
Dynamic Linking Динамическая линковка movl %edx, %esi movq %rax, %rdi call f movl G2(%rip), %eax сltq movl %edx, %esi movq %rax, %rdi call f@PLT movq G2@GOTPCREL(%rip), %rax movl (%rax), %eax cltq Недостатки статических библиотек: многократное дублирование кода в памяти связь приложения с реализацией библиотеки навсегда динамические библиотеки:.so в Unix, .dll в Windows, .dylib в MacOS X PIC (position-independent code) в Linux Procedure Linkage Table Global Offset Table ld.so – динамический загрузчик Мапирование кода в адресное пространство процесса Проблема с данными библиотеки DLL – это не PIC, как в Linux; в Windows это называется memory mapping call f … LPT f:?? a.out LPT f:0x… f() lib.so libname lib function f ld.so
Other binutils Прочие бинарные утилиты as – ассемблер ld – линкер gprof – профилировшик, требует инструментирование код ar – архиватор для создания статических библиотек (LIB – Windows) objcopy – копирует содержимое одних объектных файлов в другие objdump – получение информации из объектного файла, в частности, выполняет функцию дизассемблера readelf – показать содержимое ELF файла strip – удаляет символы из объектного файла gold – улучшенный линкер от Google, теперь – в стандартных утилитах nm – получить список символов из объектного файла c++filt – DeMangling windmc – message compiler (Win) windres – recourse compiler (Win) _ZGVZN15UICmdWithParser11parseMemoryEPPKcmRmP7ProgramP7MachineS3_RNS_15uiParserWidth_tEE8reMemory UICmdWithParser::parseMemory(char const**, unsigned long, unsigned long&, Program*, Machine*, unsigned long&, UICmdWithParser::uiParserWidth_t&)::reMemory
Libraries Библиотеки Требования к стандартной библиотеке языка Взаимодействие с ОС Удобный ввод-вывод Математические функции Средства отладки и диагностирования программ (про assert.h) Поддержка часто используемых типов (функции работы со строками, работа с UNICODE) Для С это libc ++ не все требования выполнены; например, п.1. – отдельный стандарт POSIX Распространненные реализации: GNU C Library – самая распространенная реализация, используемая в Linux Microsoft C Run-time Library Dietlibc – альтернативная небольшая реализация Стандартной библиотеки Си uClibc – Стандартная библиотека Си для встраиваемых систем на базе Linux Newlib – Стандартная библиотека языка Си для встраиваемых систем Klibc – применяется главным образом для загрузки Linux-систем Eglibc – разновидность glibc для встраиваемых систем bionic – реализация стандартной библиотеки в Android
Libraries Библиотеки Для С это libstdc++ IOStream STL Библиотеки динамической поддержки языковой; для С++ это libsupc++ EH (exception handling) RTTI (run-time type information): dynamic_cast<>, typeid , type_info new с синтаксисом размещения Библиотека поддержки компилятора; для GCC это libgcc Арифметические функции, которые не могут быть напрямую раскрыты в команды target архитектуры (divsi3(int, int)) Функции работы с исключениями (независимые от языка) (_Unwind_GetIp) Другие функции поддержки компилятора (_splitstack_find) BFD (Binary File Descriptor) library – основа большинства бинарных утилит
Creating/Poring TC Создание/Портирование СП Входные языки Два пути Создание Toolchain с нуля: есть свои плюсы, но о них почти ничего не известно (пропреитарный код, который можно продавать) Портирование имеющегося Коммерческие Front-End’ы – Edison Design Group Открытая система программирования GCC (GNU Compiler Collection) Еще одна: LLVM (Low-Level Virtual Machine) UTL (Universal Translating Library) EDG Front-End SUN Compiler MS Compiler C C++ F77 GCC LLVM Intel compiler Elbrus compiler … … CG1 CG2 Целевые платформы
Example Простой пример Простой пример на С и его возможное расположение в памяти Семантические единицы: тип и размер данных, управляющие структуры, операции, вызовы функций (что требует ABI) Это трудоемко! Описание машины быстро решает эту задачу
Machine Description (MD) Описание Машины Показан процесс сборки cc1 под ARM Сверху – блок исходных кодов, снизу – компоненты компилятора Из MD генерируется RTL generator (expander) и кодогенератор MD – описывает структуру генератора генератора кода
???(MD) Цикл работы компилятора Циклограмма работы собранного компилятора SSA (static single assignment) – представления кода, при котором каждая переменная непосредственно модифицируется лишь единожды, а далее только используется GIMPLE – высокоуровневый язык внутреннего для GCC представления программы в SSA форме RTL (register transfer language) – низкоуровневый язык внутреннего для GCC представления программы, по сути высокоуровневый ассемблер
GCC description structure Структура описания в GCC Файловая структура: директория gcc/gcc/config/<target> Файлы <target>.h, .cpp и .md , который содержит: define_insn – шаблон инструкции в генерации кода define_split – шаблон разбиения сложных шаблонов на более простые define_expand – именнованный шаблон, используется для генерации RTL из GIMPLE define_peephole – шаблон частной архитектурно-зависимой оптимизации define_predicate – шаблон предиката (для проверки соответствия операндов инструкции)
ABI Двоичный интерфейс приложения caller f() callee g() ABI (Application Binary Interface) – набор соглашений для обеспечения взаимодействия между приложениями, библиотеками и ОС Размер и выравнивание данных Формат системных вызовов Calling Convention – cпособ передачи параметров функций и возвращаемого значения: Где передавать параметры: на регистрах, в стеке, через динамическую память, комбинируя всё вышеперечисленное В каком порядке: прямом, обратном (проще реализовать эллипсиса) Кто сдвигает стек обратно: callee или caller Callee/Сaller saved регистры Какие бывают: cdecl – через стек, справа налево, обратный сдвиг – caller pascal – через стек, слева направо, сдвиг – callee fastcall – на регистрах, сдвиг – callee stdcall – через стек, справа налево, сдвиг – callee tailcall – вызов непосредственно перед возвратом, можно не двигать стек
Library Porting Портирование Библиотек Требования к библиотекам (в порядке убывания значимости) Соответствие стандарту(корректная работа). Код максимально написана на ЯВУ с минимальными аппаратными зависимостями Эффективность (Premature optimazation is the root of all evil) В идеале необходимо создать лишь машинно-зависимую часть Рассмотрим bionic. Девиз: keep it really simple! Содержит libc, libm и немного для C++ НЕ содержит поддержки механизма исключений и wide chars собирается общей системой сборки Android содержит таблицу с номерами системных вызовов и их параметрами tools/gensyscalls.py – скрипт для генерации системных вызовов В аппаратно-зависимой части находятся setjmp()/longjmp() Содержит динамический загрузчик ld.so
Simulator Симулятор Создание/портирование компилятора и ОС происходит параллельно с созданием архитектуры. Вопрос: КАК? Ответ: симулятор Функциональный симулятор Задача – отрабатывать семантику эмулируемого кода как можно быстрее QEMU – быстрый и портируемый динамический транслятор; имеет свой IR Performance симулятор Задача – воссоздать потактовую модель архитектуры предельно точно Конвейер Кэш Память Очень медленный SimPoint – обрабатывает трассы симулятора (формат BBV – Basic Block Vectors) и определяет наиболее горячие регионы исполнения для прогона на Performance симуляторе
Debugging tools Средства отладки До отладчика Static source analysis – анализ исходного кода до или во время компиляции (компилятор, утилиты lint, cppcheck) Dynamic source analysis – анализ программы на этапе исполнения; исходный код инструментируется до/во время компиляции (Insure++) Static binary analysis – анализ двоичных файлов до их запуска (Антивирусы) Dynamic binary analysis – анализ кода на этапе исполнения; инструментируется бинарный код (valgrind, Pin) Комбинированные решения Отладчик На основе аппаратной поддержки – debug registers На основе программной поддержки: INT 1 – пошаговое исполнение; INT 3 – однобайтовая команда (INT n – 2 байта) GBD (GNU DeBugger) – поддерживает оба механизма, для привязки к коду требуется отладочная информация, что требует поддержки компилятора. Есть gdb-server – для упрощения портирования.
Valgrind Valgrind Общий механизм для запуска различных утилит анализа Замедляет работы приложения в 10-50 раз По сути является JIT (Just-In-Time) компилятором (UCode – Собственный IR) Имеет ряд стандартных утилит Альтернатива – утилита Pin от Intel, настроена на x86, IA64, XScale >100 утилит для Pin. На горячем коде замедление 1.2-4 раза, на холодном 30-50 раз Memcheck – проверка памяти Cachegrind – профиль кэша Callgrind – профиль кэша+кода Massif – профиль кучи Helgrind – анализ многопоточности Lackey – кол-во инструкций и BB TreadSanitizer – новое от Google Valgrind framework IL Tool X86 PPC … Build IL init IL Code gen Instrumented IL
Performance Analysis Анализ производительности VTune – система от Intel, сбор информации о динамическом поведении приложения на основе аппаратной поддержки (множество системных регистров) Gprof – в основе лежит метод Монте-Карло: каждые 10мс прерывается исполнение, смотрим стек и добавляет 10мс ко времени исполнения процедуры Утилиты на основе valgrind (+ callgrind или cachegrind) – в основе лежит детальный подсчет инструкций, не инструментирует код, но динамически ретранслирует приложение ++ Для профилирования надо инструметрировать код (опция ‘-pg’ в GCC)
Profile example Общее число вызовов Откуда вызвали Сколько вызвали отсюда Пример профиля Место в профиле index % time self children called name ----------------------------------------------- 0.00 0.03 53/48965 BZ2_bzWriteClose64 [29] 0.00 26.95 48912/48965 BZ2_bzWrite [7] [5] 73.8 0.00 26.98 48965 BZ2_bzCompress [5] 1.81 25.17 48965/48965 handle_compress [6] 0.00 0.00 53/56 isempty_RL [43] ----------------------------------------------- 1.81 25.17 48965/48965 BZ2_bzCompress [5] [6] 73.8 1.81 25.17 48965 handle_compress [6] 5.10 19.95 273/273 BZ2_compressBlock [8] 0.12 0.00 7012881/7012881 add_pair_to_block [22] 0.00 0.00 270/273 prepare_new_block [42] 0.00 0.00 3/56 isempty_RL [43] 0.00 0.00 3/6 init_RL [47] ----------------------------------------------- 11.23 5.74 273/273 BZ2_blockSort [9] [10] 46.4 11.23 5.74 273 mainSort [10] 5.74 0.00 316455844/316455844 mainGtU [15] ----------------------------------------------- Собств. время Время потомков Листовая функция, вызывается из одного места в огромном цикле
Call Graph Граф вызовов
Instruction profiling Профиль по инструкциям
Executable Formats Исполняемые форматы ELF – Executable and Linkable Format (Unix, Linux) PE – Portable Executable (Windows) a.out – условно “непосредственный код” COFF (XCOFF, ECOFF)
Debugging formats Отладочные форматы Stab COFF PE/COFF OMF IEEE-695 DWARF – рекомендован к ознакомлению