Системы программирования (СП). Создание СП для новых

advertisement
Системы программирования (СП)
Создание СП для новых архитектур
Владиславлев Виктор
Содержание
• Системы программирования
• Структура компилятора
o
o
o
o
o
Препроцессор
Компилятор
Ассемблер
Линковщик
Динамический Линковщик
• Прочие системные утилиты
• Библиотеки
• Создание/Портирование СП
o
o
o
•
•
•
•
•
Machine Description
Двоичный интерфейс приложения
Библиотеки
Симулятор
Средства отладки
Средства анализа производительности
Форматы исполняемых файлов
Форматы отладочной информации
Agenda
Системы программирования
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
...
1
.c
.cpp
.F
...
2
High-Level
Low-Level IR
Low-Level
High-Level
IR
4
Low-Level
High-Level
IR
IR
IR
IR
asm
5
.o
.obj
6
1. 1.Препроцессор
2. 2.Front-End
3. 3.Оптимизации
4. 4.Кодогенератор
5. 5.Ассемблер
6. 6.Линкер
++ Можно проследить запуски фаз компиляции (опция ‘--verbose’ в GCC)
.out
.exe
Препроцессирование
• Инициальная обработка
1. Разбиение на строки
2. Замена триграфов
3. Объединение строк
4. Замена комментариев
• Рабиение на токены
1. (слева направо, жадная)
2. Замена диграфов (в С++)
Preprocessing
Триграф
значение
??(
??)
??<
??>
??=
??/
??'
??!
??-
[
]
{
}
#
\
^
|
~
• Собственно препроцессирование
/??/
*
*/ # /*
*/ defi\
ne FO\
O 10\
20
#define FOO 1020
a = (i++) + j;
1. Подключение файлов (#include)
2. Макроподстановки (#define/undef/##)
a = i+++j;
3. Условная компиляция (#ifdef/if/else/elif/endif)
a = i + (++j);
4. Управление строками (#line)
5. Диагностика (#error/warning)
++ Часто полезно получить препроцессированный код (опция ‘-E’ в GCC)
Препроцессирование
• Инициальная обработка
1. Разбиение на строки
2. Замена триграфов
3. Объединение строк
4. Замена комментариев
• Рабиение на токены
1. (слева направо, жадная)
2. Замена диграфов (в С++)
Preprocessing
Триграф
значение
??(
??)
??<
??>
??=
??/
??'
??!
??-
[
]
{
}
#
\
^
|
~
• Собственно препроцессирование
/??/
*
*/ # /*
*/ defi\
ne FO\
O 10\
20
#define FOO 1020
a = (i++) + j;
1. Подключение файлов (#include)
2. Макроподстановки (#define/undef/##)
a = i+++j;
3. Условная компиляция (#ifdef/if/else/elif/endif)
a = i + (++j);
4. Управление строками (#line)
5. Диагностика (#error/warning)
++ Часто полезно получить препроцессированный код (опция ‘-E’ в GCC)
собственно Компилятор
Compiler
Компилятор обрабатывает единицу трансляции (translating unit TU, compilation
unit). В С/С++ это файл после препроцессирования. В FORTRAN – один файл
может содержать несколько TU.
Компилятор включает:
1. Компилятор переднего плана (Frontend) – строит по исходному коду
промежуточное представление (Intermediate Representation – IR, или IL);
включает
o
o
o
Лексический анализатор
Синтаксический анализатор
Семантический анализатор
2. Фазы Анализа и Оптимизаций представления
o
их много (Основная часть курса), но можно и уменьшить: разные уровни оптимизаций
3. Фазы Распределение регистров и кодогенерация
Результат работы компилятора – язык ассемблера (ассемблер, машинный язык)
целевой архитектуры
++ Можно проследить результат работы компилятора после каждой фазы
(опции ‘--dump-tree-all --dump-rtl-all’ в GCC)
трансформация Кода
Code transformation
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;
int f(int b, int c)
{
int a1 = b*5 - c;
int a2 = -8*b + 2*c;
return 2*a1 + a2;
}
a1
-8
1.
2.
3.
4.
5.
Source code
AST
High-level IR
Low-level IR
assembler
set (reg:SI 62)
(ashift:SI (reg/v:SI 60 [ b ])
(const_int 1 [0x1]);
set (reg/i:SI 0 ax)
(reg:SI 62);
a2
a1
a2
ret
.globl f
.type f, @function
f:
.LFB0:
.cfi_startproc
leal (%rdi,%rdi), %eax
ret
.cfi_endproc
++ Бывает полезно посмотреть ассемблерный код (опция ‘-S’ в GCC)
Ассемблер
Assembler
Ассемблер как программа
• переводит язык ассемблера в код целевой архитектуры, сохраняемый в
исполняемом формате
• Для символьных ссылок – резервирует место, подстановку реальных адресов
осуществляет линкер
• КАК ПРАВИЛО, ассемблер – тривиален (парсинг, упаковщик). Исключение: IA64 (Itanium) (виртуальные регистры, шаблоны, скобки параллельности)
Язык ассемблера
• Имеет типы (наследованные от архитектуре), комментарии, объекты и пр.
• характерезуется тривиальным синтаксисом
• может быть не стандартизирован даже в рамках одной платформы
movl %ebx, %eax
mov eax, ebx
AT&T
Регистры начинаются с %
b, w, l, q – размер операнда
Intel
Имена регистров зарезервированы
Сначала dst, потом src
Линковщик
Linker
Дефиниция – полное определение сущности (глобала или функции)
Декларация – лишь обещание того, что где-то есть дефиниция
• Объектный файл содержит дефиниции функций и глобалов
• В коде есть ссылки на декларированные глобалы и/или функции
• Основная задача линковщика (линкера) – реализовать чужие обещания
• Проблемы: Никого не нашли – ошибка! Нескольких – (duplicate definitions):
o
o
o
С++: ‘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
Недостатки статических библиотек:
• многократное дублирование кода в памяти
• связь приложения с реализацией библиотеки навсегда
динамические библиотеки:.so в Unix, .dll в Windows, .dylib в MacOS X
PIC (position-independent code) в Linux
Procedure Linkage Table
Global Offset Table
movl %edx, %esi
ld.so – динамический загрузчик
movl %edx, %esi
movq %rax, %rdi
movq %rax, %rdi
• Мапирование кода в адресное
call f
пространство процесса
movl G2(%rip), %eax
• Проблема с данными библиотеки
сltq
call f@PLT
movq G2@GOTPCREL(%rip), %rax
movl (%rax), %eax
cltq
DLL – это не PIC, как в Linux; в Windows это называется memory mapping
call f
…
LPT
f:??
LPT
f:0x…
f()
lib.so
a.out
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
1.
2.
3.
4.
5.
Библиотеки
Libraries
• Для С это libstdc++
o
o
IOStream
STL
• Библиотеки динамической поддержки языковой; для С++ это libsupc++
o
o
o
EH (exception handling)
RTTI (run-time type information): dynamic_cast<>, typeid , type_info
new с синтаксисом размещения
• Библиотека поддержки компилятора; для GCC это libgcc
Арифметические функции, которые не могут быть напрямую раскрыты в команды
target архитектуры (divsi3(int, int))
o Функции работы с исключениями (независимые от языка) (_Unwind_GetIp)
o Другие функции поддержки компилятора (_splitstack_find)
o
• BFD (Binary File Descriptor) library – основа большинства бинарных утилит
Создание/Портирование СП
Creating/Poring TC
Два пути
• Создание Toolchain с нуля: есть свои плюсы, но о них почти ничего не известно
(пропреитарный код, который можно продавать)
• Портирование имеющегося
o Коммерческие Front-End’ы – Edison Design Group
o Открытая система программирования GCC (GNU Compiler Collection)
o Еще одна: LLVM (Low-Level Virtual Machine)
o UTL (Universal Translating Library)
Входные языки
EDG Front-End
Intel
compiler
C C++
F77
GCC
LLVM
Elbrus …
SUN
MS
Compiler
Compiler
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
GCC description structure
Файловая структура: директория gcc/gcc/config/<target>
Файлы <target>.h, .cpp и .md , который содержит:
•
•
•
•
•
define_insn – шаблон инструкции в генерации кода
define_split – шаблон разбиения сложных шаблонов на более простые
define_expand – именнованный шаблон, используется для генерации RTL из
GIMPLE
define_peephole – шаблон частной архитектурно-зависимой оптимизации
define_predicate – шаблон предиката (для проверки соответствия операндов
инструкции)
Двоичный интерфейс приложения
ABI
ABI (Application Binary Interface) – набор соглашений для обеспечения
взаимодействия между приложениями, библиотеками и ОС
• Размер и выравнивание данных
• Формат системных вызовов
• Calling Convention – cпособ передачи параметров функций и возвращаемого
значения:
o
o
o
o
Где передавать параметры: на регистрах, в стеке, через динамическую память,
комбинируя всё вышеперечисленное
caller
В каком порядке: прямом, обратном (проще реализовать эллипсиса)
f()
Кто сдвигает стек обратно: callee или caller
callee
Callee/Сaller saved регистры
g()
Какие бывают:
•
•
•
•
•
cdecl – через стек, справа налево, обратный сдвиг – caller
pascal – через стек, слева направо, сдвиг – callee
fastcall – на регистрах, сдвиг – callee
stdcall – через стек, справа налево, сдвиг – callee
tailcall – вызов непосредственно перед возвратом, можно не двигать стек
Портирование Библиотек
Library Porting
Требования к библиотекам (в порядке убывания значимости)
1. Соответствие стандарту(корректная работа).
2. Код максимально написана на ЯВУ с минимальными аппаратными зависимостями
3. Эффективность (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 раз
framework
X86
PPC
…
Build IL
init IL
IL
Code gen
Tool
Instrumented IL
Memcheck – проверка памяти
Cachegrind – профиль кэша
Callgrind – профиль кэша+кода
Massif – профиль кучи
Helgrind – анализ многопоточности
Lackey – кол-во инструкций и BB
TreadSanitizer – новое от Google
Valgrind
Анализ производительности 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)
Отладочные форматы
•
•
•
•
•
•
Stab
COFF
PE/COFF
OMF
IEEE-695
DWARF – рекомендован к ознакомлению
Debugging formats
Download