SlideShare a Scribd company logo
1 of 96
Download to read offline
Исключения C++ через призму
компиляторных оптимизаций
LLVM
Роман Русяев
Samsung R&D
сompilers developer
rusyaev.rm@gmail.com
План доклада
• Введение в реализацию исключений
• Как исключения поддержаны в LLVM
• Влияние исключений на оптимизации компилятора (на
примере LLVM)
2
Цель доклада
Как оптимизирующий компилятор работает с исключениями C++, и
как это может отразиться на производительности ваших
приложений:
• насколько дороги исключения, даже если они не выбрасываются?
3
Цель доклада
Как оптимизирующий компилятор работает с исключениями C++, и
как это может отразиться на производительности ваших
приложений:
• насколько дороги исключения, даже если они не выбрасываются?
• когда лучше исключения не использовать
4
Цель доклада
Как оптимизирующий компилятор работает с исключениями C++, и
как это может отразиться на производительности ваших
приложений:
• насколько дороги исключения, даже если они не выбрасываются?
• когда лучше исключения не использовать
• noexcept везде, где можно
5
noexcept везде, где можно
• const везде, где можно
6
noexcept везде, где можно
• const везде, где можно
• Практично ли это?
• синтаксический мусор
• с появлением семантики перемещения все стало сложнее
7
noexcept везде, где можно
• const везде, где можно
• Практично ли это?
• синтаксический мусор
• с появлением семантики перемещения все стало сложнее
• const – всегда, когда нужно (ссылки, указатели, функции члены
etc)
8
noexcept везде, где можно
• const везде, где можно
• Практично ли это?
• синтаксический мусор
• с появлением семантики перемещения все стало сложнее
• const – всегда, когда нужно (ссылки, указатели, функции члены
etc)
• А что с noexcept?
9
Введение в реализацию исключений
10
Zero-Cost Exception Handling (0eh)
• придуман для IA-64 (Itanium)
• спецификация описана в Itanium C++ ABI:
https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html
11
Zero-Cost Exception Handling (0eh)
12
не выполняется дополнительного кода, если
исключение не выбрасывается
Терминология
• спецификация исключений функции:
void foo() noexcept
void foo() throw() // deprecated
void foo() throw(type1, type2, ...) // deprecated
• cleanup
• обработчик исключения
• stack unwinding
13
stack unwinding
• stack unwinding: осуществляет вызов деструкторов локальных
объектов каждого стекового фрейма, пока не будет найден фрейм
с обработчиком исключения, соответствующим объекту
брошенного исключения.
14
stack unwinding
• stack unwinding: осуществляет вызов деструкторов локальных
объектов каждого стекового фрейма, пока не будет найден фрейм
с обработчиком исключения, соответствующим объекту
брошенного исключения. Состоит из 2х этапов:
• search phase: поиск обработчика исключения
• clean up phase: вызов деструкторов локальных объектов и передача на
обработчик исключения
15
stack unwinding, cleanup
• stack unwinding: осуществляет вызов деструкторов локальных
объектов каждого стекового фрейма, пока не будет найден фрейм
с обработчиком исключения, соответствующим объекту
брошенного исключения. Состоит из 2х этапов:
• search phase: поиск обработчика исключения
• clean up phase: вызов деструкторов локальных объектов и передача на
обработчик исключения
• cleanup: выполняет вызовы деструкторов локальных объектов в
процессе stack unwinding
16
stack unwinding, cleanup, обработчики исключений
• stack unwinding: осуществляет вызов деструкторов локальных
объектов каждого стекового фрейма, пока не будет найден фрейм
с обработчиком исключения, соответствующим объекту
брошенного исключения. Состоит из 2х этапов:
• search phase: поиск обработчика исключения
• clean up phase: вызов деструкторов локальных объектов и передача на
обработчик исключения
• cleanup: выполняет вызовы деструкторов локальных объектов в
процессе stack unwinding
• обработчик исключения: код, отвечающий за обработку
выброшенного исключения
17
std::terminate
• исключение не ловится
• не соблюден noexcept спецификатор функции
• исключение бросается из cleanup
• … (еще ~10 случаев)
18
Что происходит при бросании исключения?
19
foo
phase 1: search
…
user code
C++ runtime code
…
Что происходит при бросании исключения?
20
foo
phase 1: search
phase 2: clean up
…
user code
…
std::terminate
no handler/error
C++ runtime code
Что происходит при бросании исключения?
21
foo
phase 1: search
phase 2: clean up
… handler/clean up
user code
…
std::terminate
no handler/error
no handler/error
C++ runtime code
Что происходит при бросании исключения?
22
foo
phase 1: search
phase 2: clean up
… handler/clean up
…
user code
…
std::terminate
no handler/error
no handler/error
C++ runtime code
Что происходит при бросании исключения?
23
foo
phase 1: search
phase 2: clean up
… handler/clean up
…
user code
…
std::terminate
no handler/error
no handler/error
C++ runtime code
Что происходит при бросании исключения?
24
foo
phase 1: search
phase 2: clean up
… handler/clean up
…
user code
…
std::terminate
no handler/error
no handler/error
std::terminate
функция с noexcept
вызывает не noexcept
C++ runtime code
Поддержка исключений в LLVM
25
Введение в LLVM IR
• IR (Intermediate Representation) - структура данных или язык,
используемые внутри компилятора для отображения исходного
языка
• Будем использовать урезанный вариант LLVM IR
26
Пример инструкций на LLVM IR псевдокоде
%val ; обозначение локальной переменной или метки
alloca type ; выделить память для объекта типа type
call func_name ; вызов функции с именем func_name
ret ; инструкция возврата из функции
27
Граф потока управления (Control Flow
Graph – CFG)
BB1
BB2
BB3 BB4
BB5
…
…
28
Базовый блок (Basic Block)
instr1
instr2
…
instrN
terminator BB2
… …
BB2
…
29
Поддержка исключений в LLVM: invoke
• вызов функции с неявным переходом на участок кода, если
бросили исключение
invoke foo()
30
Поддержка исключений в LLVM: invoke
• вызов функции с неявным переходом на участок кода, если
бросили исключение
invoke foo() to label %1
31
Поддержка исключений в LLVM: invoke
• вызов функции с неявным переходом на участок кода, если
бросили исключение
invoke foo() to label %1 unwind label %2
32
Поддержка исключений в LLVM: invoke, landing pad
• вызов функции с неявным переходом на участок кода, если
бросили исключение
invoke foo() to label %1 unwind label %2
• участок кода, ответственный за обработку исключения
landingpad
33
Поддержка исключений в LLVM: invoke, landing pad, resume
• вызов функции с неявным переходом на участок кода, если
бросили исключение
invoke foo() to label %1 unwind label %2
• участок кода, ответственный за обработку исключения
landingpad
• инструкция, продолжающая раскрутку стека
resume
34
Влияние исключений на компиляторные
оптимизации
35
Накладные расходы (с точки зрения
оптимизатора)
• увеличение размера кода функции за счет создания
дополнительного кода для cleanup
• усложнение потока управления, появляющегося в результате
наличия инструкции invoke
36
Накладные расходы
Как побороть?
37
PruneEH
• преобразует инструкции invoke в call
• ставит признак nounwind для функций, которые не бросают
исключения
38
PruneEH
void extF() noexcept;
struct S {
~S() { extF(); }
};
void bar() {
extF();
}
void foo() {
S s;
bar();
}
39
void bar() {
call extF()
ret
}
PruneEH
void extF() noexcept;
struct S {
~S() { extF(); }
};
void bar() {
extF();
}
void foo() {
S s;
bar();
}
40
void bar() {
call extF()
ret
}
void foo() {
%1 = alloca S
invoke bar() to label %4 unwind label %5
…
}
PruneEH
void extF() noexcept;
struct S {
~S() { extF(); }
};
void bar() {
extF();
}
void foo() {
S s;
bar();
}
41
void bar() {
call extF()
ret
}
void foo() {
%1 = alloca S
invoke bar() to label %4 unwind label %5
; <label>:4:
call ~S(%1)
ret
; <label>:5:
landingpad ; cleanup
call ~S(%1)
resume
}
PruneEH
void foo() {
%1 = alloca S
call bar()
call ~S(%1)
ret
}
42
void foo() {
%1 = alloca S
invoke bar() to label %4 unwind label %5
; <label>:4:
call ~S(%1)
ret
; <label>:5:
landingpad ; cleanup
call ~S(%1)
resume
}
Остальные оптимизации
•Simplify the CFG
•Global Variable Optimizer
•Instruction combining
43
Накладные расходы
А где побороть не удается?
44
Inline
void foo() {
// foo actions
bar();
}
void bar() {
// bar actions
}
void foo() {
// foo actions
// bar actions
}
45
Inline: эвристики
• llvm/Analysis/InlineCost.h
• llvm/lib/Analysis/InlineCost.cpp
CallAnalyzer::analyzeBlock(…) {
…
addCost(…); // for each instruction add cost
…
}
46
Inline: эвристики
void extF();
struct MyStruct {
~MyStruct() { extF(); }
};
void bar() {
extF();
}
void bar() {
call extF()
ret
}
47
Inline: эвристики
void extF();
struct MyStruct {
~MyStruct() { extF(); }
};
void bar() {
extF();
}
void foo() {
MyStruct obj;
bar();
}
void bar() {
call extF()
ret
}
void foo() {
%0 = alloca MyStruct
invoke bar() to label %4 unwind label %5
…
}
48
Inline: эвристики
void extF();
struct MyStruct {
~MyStruct() { extF(); }
};
void bar() {
extF();
}
void foo() {
MyStruct obj;
bar();
}
void bar() {
call extF()
ret
}
void foo() {
%0 = alloca MyStruct
invoke bar() to label %4 unwind label %5
; <label>:4:
call ~MyStruct(%0)
ret
; <label>:5:
landingpad ; cleanup
call ~MyStruct(%0)
resume
}
49
Inline: эвристики
void foo() {
MyStruct obj1;
…
bar();
…
if (…) {
MyStruct obj2;
bar();
…
else {
MyStruct obj3;
bar();
…
bar();
} 50
Inline: invoke
• все инструкции call без noexcept  invoke
• поток управления от новых инструкций invoke  landing pad проинлайненной
инструкции invoke
51
invoke f2()
f1()
lpad …
before after
Inline: invoke
• все инструкции call без noexcept  invoke
• поток управления от новых инструкций invoke  landing pad проинлайненной
инструкции invoke
52
invoke f2()
f1()
call f3()
f2()
lpad …
before after
Inline: invoke
• все инструкции call без noexcept  invoke
• поток управления от новых инструкций invoke  landing pad проинлайненной
инструкции invoke
53
invoke f2()
f1()
call f3()
f2()
lpad …
invoke f3()
f1()
lpad …
before after
Inline: invoke
• все инструкции call без noexcept  invoke
• поток управления от новых инструкций invoke  landing pad проинлайненной
инструкции invoke
void foo() {
%0 = alloca MyStruct
invoke extF() to label %4 unwind label %6
; <label>:4:
call ~MyStruct(%0)
ret
; <label>:6:
landingpad
call ~MyStruct(%0)
resume
}
54
void extF();
struct MyStruct {
~MyStruct() { extF(); }
};
void bar() {
extF();
}
void foo() {
MyStruct obj;
bar();
}
Inline: resume
void extF();
struct MyStruct {
~MyStruct() { extF(); }
};
void bar() {
extF();
}
void foo() {
MyStruct obj;
bar();
}
void bar() {
call extF()
ret
}
void foo() {
%0 = alloca MyStruct
invoke bar() to label %4 unwind label %5
; <label>:4:
call ~MyStruct(%0)
ret
; <label>:5:
landingpad ; cleanup
call ~MyStruct(%0)
resume
}
55
Inline: resume
void extF();
struct MyStruct {
~MyStruct() { extF(); }
};
void bar() {
MyStruct obj;
extF();
}
void foo() {
MyStruct obj;
bar();
} 56
Inline: resume
void extF();
struct MyStruct {
~MyStruct() { extF(); }
};
void bar() {
MyStruct obj;
extF();
}
void foo() {
MyStruct obj;
bar();
}
void bar() {
call extF()
ret
}
57
void bar() {
%0 = alloca MyStruct
invoke extF() to label %4 unwind label %5
; <label>:4:
call ~MyStruct(%0)
ret
; <label>:5:
landingpad ; cleanup
call ~MyStruct(%0)
resume
}
• Инструкции resume подставляемой функции  на landing pad
проинлайненной инструкции invoke
58
Inline: resume
invoke f2()
f1() f2()
lpad1 …
before after
invoke f3()
lpad2 …
• Инструкции resume подставляемой функции  на landing pad
проинлайненной инструкции invoke
59
Inline: resume
invoke f2()
f1() f2()
lpad1 …
invoke f3()
f1()
lpad2 …
before after
invoke f3()
lpad2 …
lpad1
Inline: resume void foo() {
%1 = alloca MyStruct
%2 = alloca MyStruct
invoke extF() to label %7 unwind label %5
; <label>:5:
landingpad
call ~MyStruct(%1)
call ~MyStruct(%2)
resume
; <label>:7:
call ~MyStruct(%1)
call ~MyStruct(%2)
}
60
void bar() {
%0 = alloca MyStruct
invoke extF() to label %4 unwind label %5
; <label>:4:
call ~MyStruct(%0)
ret
; <label>:5:
landingpad ; cleanup
call ~MyStruct(%0)
resume
}
Tail Call
void foo() {
goto bar_label;
}
void bar() {
bar_label:
// bar actions
}
void foo() {
bar();
}
void bar() {
// bar actions
}
61
Tail Call
Не применима к инструкциям invoke
62
Loop Fusion
for (int i = 0; i < n; i++)
a[i] = ...;
for (int j = 0; j < n; j++)
b[j] = ...;
for (int i = 0; i < n; i++) {
a[i] = ...;
b[i] = ...;
}
63
Loop Fusion
• Если в цикле есть call, который может бросать
исключение, то цикл не рассматривается как кандидат
для оптимизации
64
LICM (Loop Invariant Code Motion)
int y = foo();
int x;
for (int i = 0; i < n; i++) {
x = y;
a[i] = x + ...;
}
int y = foo();
int x = y;
for (int i = 0; i < n; i++) {
a[i] = x + ...;
}
65
LICM (Loop Invariant Code Motion)
• не работает для инструкций invoke
• не работает для инструкций call, потенциально
бросающих исключения
66
ADCE (Aggressive Dead Code Elimination)
int global;
void f () {
int i;
i = 1;
global = 1;
global = 2;
}
67
int global;
void f () {
global = 2;
}
ADCE (Aggressive Dead Code Elimination)
bool AggressiveDeadCodeElimination::isAlwaysLive(Instruction &Inst) {
if (Inst.isEHPad() || …) { // landing pad
…
return true;
}
if (Inst.isTerminator() == false) // invoke is terminator instruction
return false;
if (isa<BranchInst>(Inst) || isa<SwitchInst>(Inst)) // invoke is not included here
return false;
return true;
}
68
Sinking
• Переносит инструкции в базовые блоки преемники, чтобы убрать
лишнее исполнение инструкций
69
def -> val
use val
before
Sinking
• Переносит инструкции в базовые блоки преемники, чтобы убрать
лишнее исполнение инструкций
70
def -> val
use val
before after
def -> val
use val
Sinking
71
bool isSafeToMove(Instruction *Inst, …) {
if (… || Inst->mayThrow())
return false;
…
}
Merged Load/Store Motion
• Объединяет инструкции записи в память по одному адресу, для
уменьшения статического размера кода
72
store addr1
before
store addr1
Merged Load/Store Motion
• Объединяет инструкции записи в память по одному адресу, для
уменьшения статического размера кода
73
store addr1
before after
store addr1
store addr1
Merged Load/Store Motion
74
/// True when instruction is a sink barrier for a store
bool MergedLoadStoreMotion::isStoreSinkBarrierInRange(const Instruction &Start,
const Instruction &End) {
for (const Instruction &Inst : make_range(Start.getIterator(), End.getIterator())) {
if (Inst.mayThrow())
return true;
}
…
}
GVNHoist
• Объединяет инструкции записи в память по одному адресу, для
уменьшения статического размера кода и сокращения критического
пути
75
load addr1
before
load addr1
GVNHoist
• Объединяет инструкции записи в память по одному адресу, для
уменьшения статического размера кода и сокращения критического
пути
76
load addr1
before after
load addr1
load addr1
GVNHoist
bool isGuaranteedToTransferExecutionToSuccessor(const Instruction *I) {
…
// Calls can throw, or contain an infinite loop, or kill the process.
If (auto CS = ImmutableCallSite(I)) {
// Call sites that throw have implicit non-local control flow.
If (!CS.doesNotThrow())
return false;
…
}
77
LICM Loop Versioning
• Дублирование циклов с проверкой на пересечение адресов и
применение LICM к копии цикла
78
runtime memcheck
LOOP LOOP COPY
LICM Loop Versioning
bool LoopVersioningLICM::instructionSafeForVersioning(Instruction *I) {
…
// Avoid loops with possiblity of throw
if (I->mayThrow())
return false;
…
}
79
Остальные оптимизации
• mayHaveSideEffects
• mayThrow
• doesNotThrow
80
Выводы
• zero-cost далеко не всегда нулевой, даже если исключение не
бросается
81
Выводы
• zero-cost далеко не всегда нулевой, даже если исключение не
бросается:
• современные компиляторы имеют специальные оптимизации для
обработки исключений
82
Выводы
• zero-cost далеко не всегда нулевой, даже если исключение не
бросается:
• современные компиляторы имеют специальные оптимизации для
обработки исключений
• если вы разрабатываете библиотеку, стоит подумать об отказе от
исключений
83
Выводы
• zero-cost далеко не всегда нулевой, даже если исключение не
бросается:
• современные компиляторы имеют специальные оптимизации для
обработки исключений
• если вы разрабатываете библиотеку, стоит подумать об отказе от
исключений
• noexcept везде, где можно
84
Выводы
• zero-cost далеко не всегда нулевой, даже если исключение не
бросается:
• современные компиляторы имеют специальные оптимизации для
обработки исключений
• если вы разрабатываете библиотеку, стоит подумать об отказе от
исключений
• noexcept везде, где можно:
• аккуратно, т.к. это часть интерфейса
85
Q & A
Роман Русяев
Samsung R&D
сompilers developer
rusyaev.rm@gmail.com
86
APPENDIX
87
setjmp/longjmp (sjlj)
• setjmp – запомнить, куда нужно прыгнуть
• longjmp – эмулирует throw
Очень непроизводительно:
• много дополнительных структур данных
• много дополнительного выполняемого кода вне зависимости от
факта бросания исключения
• Нарушение главного принципа C++ - “you only pay for what you
use”
88
Библиотека поддержки исключений
• LLVM:
• libcxxabi
• libunwind
• GCC:
• libsupc++
• libgcc
89
Библиотека поддержки исключений: throw
• _cxa_allocate_exception
• __cxa_throw:
• _Unwind_RaiseException
• …
90
Библиотека поддержки исключений: catch
• __cxa_begin_catch
• __cxa_end_catch
91
Что такое LLVM
• инфраструктура для разработки компиляторов
• компилятор и инструменты, основанные на LLVM IR
http://llvm.org
92
Введение в LLVM
• LLVM IR
• множество проектов, использующих инфраструктуру LLVM
• компилятор на основе LLVM – clang
• LLVM библиотеки (support library, command line library, …)
• алгоритмы над LLVM IR (трансформации, оптимизации, …)
• инструменты для работы с LLVM IR
• …
93
Введение в LLVM IR: основные концепции
• Модули
• Функции
• Глобальные переменные
• Метаданные
• …
94
Основы работы компилятора
frontend
middle-end
backend
compiler
input
output
95
Middle-end
• Работает с IR
• Выполняет:
• анализы
• трансформации
• оптимизации
• Проход компилятора (pass) – выполнение над IR анализа,
трансформации или оптимизации
96

More Related Content

What's hot

Основы и применение статического анализа кода при разработке лекция 1
Основы и применение статического анализа кода при разработке лекция 1Основы и применение статического анализа кода при разработке лекция 1
Основы и применение статического анализа кода при разработке лекция 1
m2rus
 
Deep Dive C# by Sergey Teplyakov
Deep Dive  C# by Sergey TeplyakovDeep Dive  C# by Sergey Teplyakov
Deep Dive C# by Sergey Teplyakov
Alex Tumanoff
 
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Platonov Sergey
 
Async clinic by by Sergey Teplyakov
Async clinic by by Sergey TeplyakovAsync clinic by by Sergey Teplyakov
Async clinic by by Sergey Teplyakov
Alex Tumanoff
 
Михаил Давыдов — JavaScript: Базовые знания
Михаил Давыдов — JavaScript: Базовые знанияМихаил Давыдов — JavaScript: Базовые знания
Михаил Давыдов — JavaScript: Базовые знания
Yandex
 

What's hot (20)

TeaVM: dead code elimination and devirtualization
TeaVM: dead code elimination and devirtualizationTeaVM: dead code elimination and devirtualization
TeaVM: dead code elimination and devirtualization
 
C++ exceptions
C++ exceptionsC++ exceptions
C++ exceptions
 
Дракон в мешке: от LLVM к C++ и проблемам неопределенного поведения
Дракон в мешке: от LLVM к C++ и проблемам неопределенного поведенияДракон в мешке: от LLVM к C++ и проблемам неопределенного поведения
Дракон в мешке: от LLVM к C++ и проблемам неопределенного поведения
 
Акторы на C++: стоило ли оно того?
Акторы на C++: стоило ли оно того?Акторы на C++: стоило ли оно того?
Акторы на C++: стоило ли оно того?
 
Основы и применение статического анализа кода при разработке лекция 1
Основы и применение статического анализа кода при разработке лекция 1Основы и применение статического анализа кода при разработке лекция 1
Основы и применение статического анализа кода при разработке лекция 1
 
Асинхронность и сопрограммы
Асинхронность и сопрограммыАсинхронность и сопрограммы
Асинхронность и сопрограммы
 
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
 
Deep Dive C# by Sergey Teplyakov
Deep Dive  C# by Sergey TeplyakovDeep Dive  C# by Sergey Teplyakov
Deep Dive C# by Sergey Teplyakov
 
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
 
Павел Беликов, Как избежать ошибок, используя современный C++
Павел Беликов, Как избежать ошибок, используя современный C++Павел Беликов, Как избежать ошибок, используя современный C++
Павел Беликов, Как избежать ошибок, используя современный C++
 
Аскетичная разработка браузера
Аскетичная разработка браузераАскетичная разработка браузера
Аскетичная разработка браузера
 
Григорий Демченко, Асинхронность и неблокирующая синхронизация
Григорий Демченко, Асинхронность и неблокирующая синхронизацияГригорий Демченко, Асинхронность и неблокирующая синхронизация
Григорий Демченко, Асинхронность и неблокирующая синхронизация
 
Async clinic by by Sergey Teplyakov
Async clinic by by Sergey TeplyakovAsync clinic by by Sergey Teplyakov
Async clinic by by Sergey Teplyakov
 
Борис Сазонов, RAII потоки и CancellationToken в C++
Борис Сазонов, RAII потоки и CancellationToken в C++Борис Сазонов, RAII потоки и CancellationToken в C++
Борис Сазонов, RAII потоки и CancellationToken в C++
 
Евгений Зуев, С++ в России: Стандарт языка и его реализация
Евгений Зуев, С++ в России: Стандарт языка и его реализацияЕвгений Зуев, С++ в России: Стандарт языка и его реализация
Евгений Зуев, С++ в России: Стандарт языка и его реализация
 
Михаил Давыдов — JavaScript: Базовые знания
Михаил Давыдов — JavaScript: Базовые знанияМихаил Давыдов — JavaScript: Базовые знания
Михаил Давыдов — JavaScript: Базовые знания
 
Объектно-ориентированное программирование. Лекция 5 и 6
Объектно-ориентированное программирование. Лекция 5 и 6Объектно-ориентированное программирование. Лекция 5 и 6
Объектно-ориентированное программирование. Лекция 5 и 6
 
Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...
Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...
Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...
 
Для чего мы делали свой акторный фреймворк и что из этого вышло?
Для чего мы делали свой акторный фреймворк и что из этого вышло?Для чего мы делали свой акторный фреймворк и что из этого вышло?
Для чего мы делали свой акторный фреймворк и что из этого вышло?
 
хитрости выведения типов
хитрости выведения типовхитрости выведения типов
хитрости выведения типов
 

Similar to Исключения C++ через призму компиляторных оптимизаций. Роман Русяев ➠ CoreHard Autumn 2019

Tech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVMTech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU
 
Что могут статические анализаторы, чего не могут программисты и тестировщики
Что могут статические анализаторы, чего не могут программисты и тестировщикиЧто могут статические анализаторы, чего не могут программисты и тестировщики
Что могут статические анализаторы, чего не могут программисты и тестировщики
Andrey Karpov
 
ekbpy'2012 - Михаил Коробов - Python 3
ekbpy'2012 - Михаил Коробов - Python 3ekbpy'2012 - Михаил Коробов - Python 3
ekbpy'2012 - Михаил Коробов - Python 3
it-people
 

Similar to Исключения C++ через призму компиляторных оптимизаций. Роман Русяев ➠ CoreHard Autumn 2019 (20)

Static code analysis of the projects built on Unreal Engine
Static code analysis of the projects built on Unreal EngineStatic code analysis of the projects built on Unreal Engine
Static code analysis of the projects built on Unreal Engine
 
Статический анализ кода проектов, построенных на движке Unreal Engine
Статический анализ кода проектов, построенных на движке Unreal EngineСтатический анализ кода проектов, построенных на движке Unreal Engine
Статический анализ кода проектов, построенных на движке Unreal Engine
 
Александр Фокин, Рефлексия в C++
Александр Фокин, Рефлексия в C++Александр Фокин, Рефлексия в C++
Александр Фокин, Рефлексия в C++
 
Поддержка Java 8 в Excelsior JET
Поддержка Java 8 в Excelsior JET Поддержка Java 8 в Excelsior JET
Поддержка Java 8 в Excelsior JET
 
Принципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioПринципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-Studio
 
Как мы учились чинить самолеты в воздухе / Евгений Коломеец (Virtuozzo)
Как мы учились чинить самолеты в воздухе / Евгений Коломеец (Virtuozzo)Как мы учились чинить самолеты в воздухе / Евгений Коломеец (Virtuozzo)
Как мы учились чинить самолеты в воздухе / Евгений Коломеец (Virtuozzo)
 
Tech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVMTech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVM
 
Как приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVMКак приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVM
 
Контейнер сервисов
Контейнер сервисовКонтейнер сервисов
Контейнер сервисов
 
200 open source проектов спустя: опыт статического анализа исходного кода
200 open source проектов спустя:опыт статического анализа исходного кода200 open source проектов спустя:опыт статического анализа исходного кода
200 open source проектов спустя: опыт статического анализа исходного кода
 
200 open source проектов спустя: опыт статического анализа исходного кода
200 open source проектов спустя: опыт статического анализа исходного кода200 open source проектов спустя: опыт статического анализа исходного кода
200 open source проектов спустя: опыт статического анализа исходного кода
 
Статический анализ и написание качественного кода на C/C++ для встраиваемых с...
Статический анализ и написание качественного кода на C/C++ для встраиваемых с...Статический анализ и написание качественного кода на C/C++ для встраиваемых с...
Статический анализ и написание качественного кода на C/C++ для встраиваемых с...
 
Что могут статические анализаторы, чего не могут программисты и тестировщики
Что могут статические анализаторы, чего не могут программисты и тестировщикиЧто могут статические анализаторы, чего не могут программисты и тестировщики
Что могут статические анализаторы, чего не могут программисты и тестировщики
 
Delegates and events in C#
Delegates and events in C#Delegates and events in C#
Delegates and events in C#
 
Построение компилятора на базе LLVM — Павел Сычев
 Построение компилятора на базе LLVM — Павел Сычев Построение компилятора на базе LLVM — Павел Сычев
Построение компилятора на базе LLVM — Павел Сычев
 
ekbpy'2012 - Михаил Коробов - Python 3
ekbpy'2012 - Михаил Коробов - Python 3ekbpy'2012 - Михаил Коробов - Python 3
ekbpy'2012 - Михаил Коробов - Python 3
 
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
 
Java 8 Support at the JVM Level
Java 8 Support at the JVM LevelJava 8 Support at the JVM Level
Java 8 Support at the JVM Level
 
Статические анализаторы кода как DevSecOps решение
Статические анализаторы кода как DevSecOps решениеСтатические анализаторы кода как DevSecOps решение
Статические анализаторы кода как DevSecOps решение
 
Сверхоптимизация кода на Python
Сверхоптимизация кода на PythonСверхоптимизация кода на Python
Сверхоптимизация кода на Python
 

More from corehard_by

C++ CoreHard Autumn 2018. Обработка списков на C++ в функциональном стиле - В...
C++ CoreHard Autumn 2018. Обработка списков на C++ в функциональном стиле - В...C++ CoreHard Autumn 2018. Обработка списков на C++ в функциональном стиле - В...
C++ CoreHard Autumn 2018. Обработка списков на C++ в функциональном стиле - В...
corehard_by
 
C++ CoreHard Autumn 2018. Debug C++ Without Running - Anastasia Kazakova
C++ CoreHard Autumn 2018. Debug C++ Without Running - Anastasia KazakovaC++ CoreHard Autumn 2018. Debug C++ Without Running - Anastasia Kazakova
C++ CoreHard Autumn 2018. Debug C++ Without Running - Anastasia Kazakova
corehard_by
 
C++ CoreHard Autumn 2018. Text Formatting For a Future Range-Based Standard L...
C++ CoreHard Autumn 2018. Text Formatting For a Future Range-Based Standard L...C++ CoreHard Autumn 2018. Text Formatting For a Future Range-Based Standard L...
C++ CoreHard Autumn 2018. Text Formatting For a Future Range-Based Standard L...
corehard_by
 

More from corehard_by (20)

C++ CoreHard Autumn 2018. Создание пакетов для открытых библиотек через conan...
C++ CoreHard Autumn 2018. Создание пакетов для открытых библиотек через conan...C++ CoreHard Autumn 2018. Создание пакетов для открытых библиотек через conan...
C++ CoreHard Autumn 2018. Создание пакетов для открытых библиотек через conan...
 
C++ CoreHard Autumn 2018. Что должен знать каждый C++ программист или Как про...
C++ CoreHard Autumn 2018. Что должен знать каждый C++ программист или Как про...C++ CoreHard Autumn 2018. Что должен знать каждый C++ программист или Как про...
C++ CoreHard Autumn 2018. Что должен знать каждый C++ программист или Как про...
 
C++ CoreHard Autumn 2018. Actors vs CSP vs Tasks vs ... - Евгений Охотников
C++ CoreHard Autumn 2018. Actors vs CSP vs Tasks vs ... - Евгений ОхотниковC++ CoreHard Autumn 2018. Actors vs CSP vs Tasks vs ... - Евгений Охотников
C++ CoreHard Autumn 2018. Actors vs CSP vs Tasks vs ... - Евгений Охотников
 
C++ CoreHard Autumn 2018. Знай свое "железо": иерархия памяти - Александр Титов
C++ CoreHard Autumn 2018. Знай свое "железо": иерархия памяти - Александр ТитовC++ CoreHard Autumn 2018. Знай свое "железо": иерархия памяти - Александр Титов
C++ CoreHard Autumn 2018. Знай свое "железо": иерархия памяти - Александр Титов
 
C++ CoreHard Autumn 2018. Информационная безопасность и разработка ПО - Евген...
C++ CoreHard Autumn 2018. Информационная безопасность и разработка ПО - Евген...C++ CoreHard Autumn 2018. Информационная безопасность и разработка ПО - Евген...
C++ CoreHard Autumn 2018. Информационная безопасность и разработка ПО - Евген...
 
C++ CoreHard Autumn 2018. Заглядываем под капот «Поясов по C++» - Илья Шишков
C++ CoreHard Autumn 2018. Заглядываем под капот «Поясов по C++» - Илья ШишковC++ CoreHard Autumn 2018. Заглядываем под капот «Поясов по C++» - Илья Шишков
C++ CoreHard Autumn 2018. Заглядываем под капот «Поясов по C++» - Илья Шишков
 
C++ CoreHard Autumn 2018. Ускорение сборки C++ проектов, способы и последстви...
C++ CoreHard Autumn 2018. Ускорение сборки C++ проектов, способы и последстви...C++ CoreHard Autumn 2018. Ускорение сборки C++ проектов, способы и последстви...
C++ CoreHard Autumn 2018. Ускорение сборки C++ проектов, способы и последстви...
 
C++ CoreHard Autumn 2018. Метаклассы: воплощаем мечты в реальность - Сергей С...
C++ CoreHard Autumn 2018. Метаклассы: воплощаем мечты в реальность - Сергей С...C++ CoreHard Autumn 2018. Метаклассы: воплощаем мечты в реальность - Сергей С...
C++ CoreHard Autumn 2018. Метаклассы: воплощаем мечты в реальность - Сергей С...
 
C++ CoreHard Autumn 2018. Что не умеет оптимизировать компилятор - Александр ...
C++ CoreHard Autumn 2018. Что не умеет оптимизировать компилятор - Александр ...C++ CoreHard Autumn 2018. Что не умеет оптимизировать компилятор - Александр ...
C++ CoreHard Autumn 2018. Что не умеет оптимизировать компилятор - Александр ...
 
C++ CoreHard Autumn 2018. Кодогенерация C++ кроссплатформенно. Продолжение - ...
C++ CoreHard Autumn 2018. Кодогенерация C++ кроссплатформенно. Продолжение - ...C++ CoreHard Autumn 2018. Кодогенерация C++ кроссплатформенно. Продолжение - ...
C++ CoreHard Autumn 2018. Кодогенерация C++ кроссплатформенно. Продолжение - ...
 
C++ CoreHard Autumn 2018. Concurrency and Parallelism in C++17 and C++20/23 -...
C++ CoreHard Autumn 2018. Concurrency and Parallelism in C++17 and C++20/23 -...C++ CoreHard Autumn 2018. Concurrency and Parallelism in C++17 and C++20/23 -...
C++ CoreHard Autumn 2018. Concurrency and Parallelism in C++17 and C++20/23 -...
 
C++ CoreHard Autumn 2018. Обработка списков на C++ в функциональном стиле - В...
C++ CoreHard Autumn 2018. Обработка списков на C++ в функциональном стиле - В...C++ CoreHard Autumn 2018. Обработка списков на C++ в функциональном стиле - В...
C++ CoreHard Autumn 2018. Обработка списков на C++ в функциональном стиле - В...
 
C++ Corehard Autumn 2018. Обучаем на Python, применяем на C++ - Павел Филонов
C++ Corehard Autumn 2018. Обучаем на Python, применяем на C++ - Павел ФилоновC++ Corehard Autumn 2018. Обучаем на Python, применяем на C++ - Павел Филонов
C++ Corehard Autumn 2018. Обучаем на Python, применяем на C++ - Павел Филонов
 
C++ CoreHard Autumn 2018. Asynchronous programming with ranges - Ivan Čukić
C++ CoreHard Autumn 2018. Asynchronous programming with ranges - Ivan ČukićC++ CoreHard Autumn 2018. Asynchronous programming with ranges - Ivan Čukić
C++ CoreHard Autumn 2018. Asynchronous programming with ranges - Ivan Čukić
 
C++ CoreHard Autumn 2018. Debug C++ Without Running - Anastasia Kazakova
C++ CoreHard Autumn 2018. Debug C++ Without Running - Anastasia KazakovaC++ CoreHard Autumn 2018. Debug C++ Without Running - Anastasia Kazakova
C++ CoreHard Autumn 2018. Debug C++ Without Running - Anastasia Kazakova
 
C++ CoreHard Autumn 2018. Полезный constexpr - Антон Полухин
C++ CoreHard Autumn 2018. Полезный constexpr - Антон ПолухинC++ CoreHard Autumn 2018. Полезный constexpr - Антон Полухин
C++ CoreHard Autumn 2018. Полезный constexpr - Антон Полухин
 
C++ CoreHard Autumn 2018. Text Formatting For a Future Range-Based Standard L...
C++ CoreHard Autumn 2018. Text Formatting For a Future Range-Based Standard L...C++ CoreHard Autumn 2018. Text Formatting For a Future Range-Based Standard L...
C++ CoreHard Autumn 2018. Text Formatting For a Future Range-Based Standard L...
 
Исключительная модель памяти. Алексей Ткаченко ➠ CoreHard Autumn 2019
Исключительная модель памяти. Алексей Ткаченко ➠ CoreHard Autumn 2019Исключительная модель памяти. Алексей Ткаченко ➠ CoreHard Autumn 2019
Исключительная модель памяти. Алексей Ткаченко ➠ CoreHard Autumn 2019
 
Как помочь и как помешать компилятору. Андрей Олейников ➠ CoreHard Autumn 2019
Как помочь и как помешать компилятору. Андрей Олейников ➠  CoreHard Autumn 2019Как помочь и как помешать компилятору. Андрей Олейников ➠  CoreHard Autumn 2019
Как помочь и как помешать компилятору. Андрей Олейников ➠ CoreHard Autumn 2019
 
Автоматизируй это. Кирилл Тихонов ➠ CoreHard Autumn 2019
Автоматизируй это. Кирилл Тихонов ➠  CoreHard Autumn 2019Автоматизируй это. Кирилл Тихонов ➠  CoreHard Autumn 2019
Автоматизируй это. Кирилл Тихонов ➠ CoreHard Autumn 2019
 

Исключения C++ через призму компиляторных оптимизаций. Роман Русяев ➠ CoreHard Autumn 2019

  • 1. Исключения C++ через призму компиляторных оптимизаций LLVM Роман Русяев Samsung R&D сompilers developer rusyaev.rm@gmail.com
  • 2. План доклада • Введение в реализацию исключений • Как исключения поддержаны в LLVM • Влияние исключений на оптимизации компилятора (на примере LLVM) 2
  • 3. Цель доклада Как оптимизирующий компилятор работает с исключениями C++, и как это может отразиться на производительности ваших приложений: • насколько дороги исключения, даже если они не выбрасываются? 3
  • 4. Цель доклада Как оптимизирующий компилятор работает с исключениями C++, и как это может отразиться на производительности ваших приложений: • насколько дороги исключения, даже если они не выбрасываются? • когда лучше исключения не использовать 4
  • 5. Цель доклада Как оптимизирующий компилятор работает с исключениями C++, и как это может отразиться на производительности ваших приложений: • насколько дороги исключения, даже если они не выбрасываются? • когда лучше исключения не использовать • noexcept везде, где можно 5
  • 6. noexcept везде, где можно • const везде, где можно 6
  • 7. noexcept везде, где можно • const везде, где можно • Практично ли это? • синтаксический мусор • с появлением семантики перемещения все стало сложнее 7
  • 8. noexcept везде, где можно • const везде, где можно • Практично ли это? • синтаксический мусор • с появлением семантики перемещения все стало сложнее • const – всегда, когда нужно (ссылки, указатели, функции члены etc) 8
  • 9. noexcept везде, где можно • const везде, где можно • Практично ли это? • синтаксический мусор • с появлением семантики перемещения все стало сложнее • const – всегда, когда нужно (ссылки, указатели, функции члены etc) • А что с noexcept? 9
  • 10. Введение в реализацию исключений 10
  • 11. Zero-Cost Exception Handling (0eh) • придуман для IA-64 (Itanium) • спецификация описана в Itanium C++ ABI: https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html 11
  • 12. Zero-Cost Exception Handling (0eh) 12 не выполняется дополнительного кода, если исключение не выбрасывается
  • 13. Терминология • спецификация исключений функции: void foo() noexcept void foo() throw() // deprecated void foo() throw(type1, type2, ...) // deprecated • cleanup • обработчик исключения • stack unwinding 13
  • 14. stack unwinding • stack unwinding: осуществляет вызов деструкторов локальных объектов каждого стекового фрейма, пока не будет найден фрейм с обработчиком исключения, соответствующим объекту брошенного исключения. 14
  • 15. stack unwinding • stack unwinding: осуществляет вызов деструкторов локальных объектов каждого стекового фрейма, пока не будет найден фрейм с обработчиком исключения, соответствующим объекту брошенного исключения. Состоит из 2х этапов: • search phase: поиск обработчика исключения • clean up phase: вызов деструкторов локальных объектов и передача на обработчик исключения 15
  • 16. stack unwinding, cleanup • stack unwinding: осуществляет вызов деструкторов локальных объектов каждого стекового фрейма, пока не будет найден фрейм с обработчиком исключения, соответствующим объекту брошенного исключения. Состоит из 2х этапов: • search phase: поиск обработчика исключения • clean up phase: вызов деструкторов локальных объектов и передача на обработчик исключения • cleanup: выполняет вызовы деструкторов локальных объектов в процессе stack unwinding 16
  • 17. stack unwinding, cleanup, обработчики исключений • stack unwinding: осуществляет вызов деструкторов локальных объектов каждого стекового фрейма, пока не будет найден фрейм с обработчиком исключения, соответствующим объекту брошенного исключения. Состоит из 2х этапов: • search phase: поиск обработчика исключения • clean up phase: вызов деструкторов локальных объектов и передача на обработчик исключения • cleanup: выполняет вызовы деструкторов локальных объектов в процессе stack unwinding • обработчик исключения: код, отвечающий за обработку выброшенного исключения 17
  • 18. std::terminate • исключение не ловится • не соблюден noexcept спецификатор функции • исключение бросается из cleanup • … (еще ~10 случаев) 18
  • 19. Что происходит при бросании исключения? 19 foo phase 1: search … user code C++ runtime code …
  • 20. Что происходит при бросании исключения? 20 foo phase 1: search phase 2: clean up … user code … std::terminate no handler/error C++ runtime code
  • 21. Что происходит при бросании исключения? 21 foo phase 1: search phase 2: clean up … handler/clean up user code … std::terminate no handler/error no handler/error C++ runtime code
  • 22. Что происходит при бросании исключения? 22 foo phase 1: search phase 2: clean up … handler/clean up … user code … std::terminate no handler/error no handler/error C++ runtime code
  • 23. Что происходит при бросании исключения? 23 foo phase 1: search phase 2: clean up … handler/clean up … user code … std::terminate no handler/error no handler/error C++ runtime code
  • 24. Что происходит при бросании исключения? 24 foo phase 1: search phase 2: clean up … handler/clean up … user code … std::terminate no handler/error no handler/error std::terminate функция с noexcept вызывает не noexcept C++ runtime code
  • 26. Введение в LLVM IR • IR (Intermediate Representation) - структура данных или язык, используемые внутри компилятора для отображения исходного языка • Будем использовать урезанный вариант LLVM IR 26
  • 27. Пример инструкций на LLVM IR псевдокоде %val ; обозначение локальной переменной или метки alloca type ; выделить память для объекта типа type call func_name ; вызов функции с именем func_name ret ; инструкция возврата из функции 27
  • 28. Граф потока управления (Control Flow Graph – CFG) BB1 BB2 BB3 BB4 BB5 … … 28
  • 29. Базовый блок (Basic Block) instr1 instr2 … instrN terminator BB2 … … BB2 … 29
  • 30. Поддержка исключений в LLVM: invoke • вызов функции с неявным переходом на участок кода, если бросили исключение invoke foo() 30
  • 31. Поддержка исключений в LLVM: invoke • вызов функции с неявным переходом на участок кода, если бросили исключение invoke foo() to label %1 31
  • 32. Поддержка исключений в LLVM: invoke • вызов функции с неявным переходом на участок кода, если бросили исключение invoke foo() to label %1 unwind label %2 32
  • 33. Поддержка исключений в LLVM: invoke, landing pad • вызов функции с неявным переходом на участок кода, если бросили исключение invoke foo() to label %1 unwind label %2 • участок кода, ответственный за обработку исключения landingpad 33
  • 34. Поддержка исключений в LLVM: invoke, landing pad, resume • вызов функции с неявным переходом на участок кода, если бросили исключение invoke foo() to label %1 unwind label %2 • участок кода, ответственный за обработку исключения landingpad • инструкция, продолжающая раскрутку стека resume 34
  • 35. Влияние исключений на компиляторные оптимизации 35
  • 36. Накладные расходы (с точки зрения оптимизатора) • увеличение размера кода функции за счет создания дополнительного кода для cleanup • усложнение потока управления, появляющегося в результате наличия инструкции invoke 36
  • 38. PruneEH • преобразует инструкции invoke в call • ставит признак nounwind для функций, которые не бросают исключения 38
  • 39. PruneEH void extF() noexcept; struct S { ~S() { extF(); } }; void bar() { extF(); } void foo() { S s; bar(); } 39 void bar() { call extF() ret }
  • 40. PruneEH void extF() noexcept; struct S { ~S() { extF(); } }; void bar() { extF(); } void foo() { S s; bar(); } 40 void bar() { call extF() ret } void foo() { %1 = alloca S invoke bar() to label %4 unwind label %5 … }
  • 41. PruneEH void extF() noexcept; struct S { ~S() { extF(); } }; void bar() { extF(); } void foo() { S s; bar(); } 41 void bar() { call extF() ret } void foo() { %1 = alloca S invoke bar() to label %4 unwind label %5 ; <label>:4: call ~S(%1) ret ; <label>:5: landingpad ; cleanup call ~S(%1) resume }
  • 42. PruneEH void foo() { %1 = alloca S call bar() call ~S(%1) ret } 42 void foo() { %1 = alloca S invoke bar() to label %4 unwind label %5 ; <label>:4: call ~S(%1) ret ; <label>:5: landingpad ; cleanup call ~S(%1) resume }
  • 43. Остальные оптимизации •Simplify the CFG •Global Variable Optimizer •Instruction combining 43
  • 44. Накладные расходы А где побороть не удается? 44
  • 45. Inline void foo() { // foo actions bar(); } void bar() { // bar actions } void foo() { // foo actions // bar actions } 45
  • 46. Inline: эвристики • llvm/Analysis/InlineCost.h • llvm/lib/Analysis/InlineCost.cpp CallAnalyzer::analyzeBlock(…) { … addCost(…); // for each instruction add cost … } 46
  • 47. Inline: эвристики void extF(); struct MyStruct { ~MyStruct() { extF(); } }; void bar() { extF(); } void bar() { call extF() ret } 47
  • 48. Inline: эвристики void extF(); struct MyStruct { ~MyStruct() { extF(); } }; void bar() { extF(); } void foo() { MyStruct obj; bar(); } void bar() { call extF() ret } void foo() { %0 = alloca MyStruct invoke bar() to label %4 unwind label %5 … } 48
  • 49. Inline: эвристики void extF(); struct MyStruct { ~MyStruct() { extF(); } }; void bar() { extF(); } void foo() { MyStruct obj; bar(); } void bar() { call extF() ret } void foo() { %0 = alloca MyStruct invoke bar() to label %4 unwind label %5 ; <label>:4: call ~MyStruct(%0) ret ; <label>:5: landingpad ; cleanup call ~MyStruct(%0) resume } 49
  • 50. Inline: эвристики void foo() { MyStruct obj1; … bar(); … if (…) { MyStruct obj2; bar(); … else { MyStruct obj3; bar(); … bar(); } 50
  • 51. Inline: invoke • все инструкции call без noexcept  invoke • поток управления от новых инструкций invoke  landing pad проинлайненной инструкции invoke 51 invoke f2() f1() lpad … before after
  • 52. Inline: invoke • все инструкции call без noexcept  invoke • поток управления от новых инструкций invoke  landing pad проинлайненной инструкции invoke 52 invoke f2() f1() call f3() f2() lpad … before after
  • 53. Inline: invoke • все инструкции call без noexcept  invoke • поток управления от новых инструкций invoke  landing pad проинлайненной инструкции invoke 53 invoke f2() f1() call f3() f2() lpad … invoke f3() f1() lpad … before after
  • 54. Inline: invoke • все инструкции call без noexcept  invoke • поток управления от новых инструкций invoke  landing pad проинлайненной инструкции invoke void foo() { %0 = alloca MyStruct invoke extF() to label %4 unwind label %6 ; <label>:4: call ~MyStruct(%0) ret ; <label>:6: landingpad call ~MyStruct(%0) resume } 54 void extF(); struct MyStruct { ~MyStruct() { extF(); } }; void bar() { extF(); } void foo() { MyStruct obj; bar(); }
  • 55. Inline: resume void extF(); struct MyStruct { ~MyStruct() { extF(); } }; void bar() { extF(); } void foo() { MyStruct obj; bar(); } void bar() { call extF() ret } void foo() { %0 = alloca MyStruct invoke bar() to label %4 unwind label %5 ; <label>:4: call ~MyStruct(%0) ret ; <label>:5: landingpad ; cleanup call ~MyStruct(%0) resume } 55
  • 56. Inline: resume void extF(); struct MyStruct { ~MyStruct() { extF(); } }; void bar() { MyStruct obj; extF(); } void foo() { MyStruct obj; bar(); } 56
  • 57. Inline: resume void extF(); struct MyStruct { ~MyStruct() { extF(); } }; void bar() { MyStruct obj; extF(); } void foo() { MyStruct obj; bar(); } void bar() { call extF() ret } 57 void bar() { %0 = alloca MyStruct invoke extF() to label %4 unwind label %5 ; <label>:4: call ~MyStruct(%0) ret ; <label>:5: landingpad ; cleanup call ~MyStruct(%0) resume }
  • 58. • Инструкции resume подставляемой функции  на landing pad проинлайненной инструкции invoke 58 Inline: resume invoke f2() f1() f2() lpad1 … before after invoke f3() lpad2 …
  • 59. • Инструкции resume подставляемой функции  на landing pad проинлайненной инструкции invoke 59 Inline: resume invoke f2() f1() f2() lpad1 … invoke f3() f1() lpad2 … before after invoke f3() lpad2 … lpad1
  • 60. Inline: resume void foo() { %1 = alloca MyStruct %2 = alloca MyStruct invoke extF() to label %7 unwind label %5 ; <label>:5: landingpad call ~MyStruct(%1) call ~MyStruct(%2) resume ; <label>:7: call ~MyStruct(%1) call ~MyStruct(%2) } 60 void bar() { %0 = alloca MyStruct invoke extF() to label %4 unwind label %5 ; <label>:4: call ~MyStruct(%0) ret ; <label>:5: landingpad ; cleanup call ~MyStruct(%0) resume }
  • 61. Tail Call void foo() { goto bar_label; } void bar() { bar_label: // bar actions } void foo() { bar(); } void bar() { // bar actions } 61
  • 62. Tail Call Не применима к инструкциям invoke 62
  • 63. Loop Fusion for (int i = 0; i < n; i++) a[i] = ...; for (int j = 0; j < n; j++) b[j] = ...; for (int i = 0; i < n; i++) { a[i] = ...; b[i] = ...; } 63
  • 64. Loop Fusion • Если в цикле есть call, который может бросать исключение, то цикл не рассматривается как кандидат для оптимизации 64
  • 65. LICM (Loop Invariant Code Motion) int y = foo(); int x; for (int i = 0; i < n; i++) { x = y; a[i] = x + ...; } int y = foo(); int x = y; for (int i = 0; i < n; i++) { a[i] = x + ...; } 65
  • 66. LICM (Loop Invariant Code Motion) • не работает для инструкций invoke • не работает для инструкций call, потенциально бросающих исключения 66
  • 67. ADCE (Aggressive Dead Code Elimination) int global; void f () { int i; i = 1; global = 1; global = 2; } 67 int global; void f () { global = 2; }
  • 68. ADCE (Aggressive Dead Code Elimination) bool AggressiveDeadCodeElimination::isAlwaysLive(Instruction &Inst) { if (Inst.isEHPad() || …) { // landing pad … return true; } if (Inst.isTerminator() == false) // invoke is terminator instruction return false; if (isa<BranchInst>(Inst) || isa<SwitchInst>(Inst)) // invoke is not included here return false; return true; } 68
  • 69. Sinking • Переносит инструкции в базовые блоки преемники, чтобы убрать лишнее исполнение инструкций 69 def -> val use val before
  • 70. Sinking • Переносит инструкции в базовые блоки преемники, чтобы убрать лишнее исполнение инструкций 70 def -> val use val before after def -> val use val
  • 71. Sinking 71 bool isSafeToMove(Instruction *Inst, …) { if (… || Inst->mayThrow()) return false; … }
  • 72. Merged Load/Store Motion • Объединяет инструкции записи в память по одному адресу, для уменьшения статического размера кода 72 store addr1 before store addr1
  • 73. Merged Load/Store Motion • Объединяет инструкции записи в память по одному адресу, для уменьшения статического размера кода 73 store addr1 before after store addr1 store addr1
  • 74. Merged Load/Store Motion 74 /// True when instruction is a sink barrier for a store bool MergedLoadStoreMotion::isStoreSinkBarrierInRange(const Instruction &Start, const Instruction &End) { for (const Instruction &Inst : make_range(Start.getIterator(), End.getIterator())) { if (Inst.mayThrow()) return true; } … }
  • 75. GVNHoist • Объединяет инструкции записи в память по одному адресу, для уменьшения статического размера кода и сокращения критического пути 75 load addr1 before load addr1
  • 76. GVNHoist • Объединяет инструкции записи в память по одному адресу, для уменьшения статического размера кода и сокращения критического пути 76 load addr1 before after load addr1 load addr1
  • 77. GVNHoist bool isGuaranteedToTransferExecutionToSuccessor(const Instruction *I) { … // Calls can throw, or contain an infinite loop, or kill the process. If (auto CS = ImmutableCallSite(I)) { // Call sites that throw have implicit non-local control flow. If (!CS.doesNotThrow()) return false; … } 77
  • 78. LICM Loop Versioning • Дублирование циклов с проверкой на пересечение адресов и применение LICM к копии цикла 78 runtime memcheck LOOP LOOP COPY
  • 79. LICM Loop Versioning bool LoopVersioningLICM::instructionSafeForVersioning(Instruction *I) { … // Avoid loops with possiblity of throw if (I->mayThrow()) return false; … } 79
  • 81. Выводы • zero-cost далеко не всегда нулевой, даже если исключение не бросается 81
  • 82. Выводы • zero-cost далеко не всегда нулевой, даже если исключение не бросается: • современные компиляторы имеют специальные оптимизации для обработки исключений 82
  • 83. Выводы • zero-cost далеко не всегда нулевой, даже если исключение не бросается: • современные компиляторы имеют специальные оптимизации для обработки исключений • если вы разрабатываете библиотеку, стоит подумать об отказе от исключений 83
  • 84. Выводы • zero-cost далеко не всегда нулевой, даже если исключение не бросается: • современные компиляторы имеют специальные оптимизации для обработки исключений • если вы разрабатываете библиотеку, стоит подумать об отказе от исключений • noexcept везде, где можно 84
  • 85. Выводы • zero-cost далеко не всегда нулевой, даже если исключение не бросается: • современные компиляторы имеют специальные оптимизации для обработки исключений • если вы разрабатываете библиотеку, стоит подумать об отказе от исключений • noexcept везде, где можно: • аккуратно, т.к. это часть интерфейса 85
  • 86. Q & A Роман Русяев Samsung R&D сompilers developer rusyaev.rm@gmail.com 86
  • 88. setjmp/longjmp (sjlj) • setjmp – запомнить, куда нужно прыгнуть • longjmp – эмулирует throw Очень непроизводительно: • много дополнительных структур данных • много дополнительного выполняемого кода вне зависимости от факта бросания исключения • Нарушение главного принципа C++ - “you only pay for what you use” 88
  • 89. Библиотека поддержки исключений • LLVM: • libcxxabi • libunwind • GCC: • libsupc++ • libgcc 89
  • 90. Библиотека поддержки исключений: throw • _cxa_allocate_exception • __cxa_throw: • _Unwind_RaiseException • … 90
  • 91. Библиотека поддержки исключений: catch • __cxa_begin_catch • __cxa_end_catch 91
  • 92. Что такое LLVM • инфраструктура для разработки компиляторов • компилятор и инструменты, основанные на LLVM IR http://llvm.org 92
  • 93. Введение в LLVM • LLVM IR • множество проектов, использующих инфраструктуру LLVM • компилятор на основе LLVM – clang • LLVM библиотеки (support library, command line library, …) • алгоритмы над LLVM IR (трансформации, оптимизации, …) • инструменты для работы с LLVM IR • … 93
  • 94. Введение в LLVM IR: основные концепции • Модули • Функции • Глобальные переменные • Метаданные • … 94
  • 96. Middle-end • Работает с IR • Выполняет: • анализы • трансформации • оптимизации • Проход компилятора (pass) – выполнение над IR анализа, трансформации или оптимизации 96